@@ -43,6 +43,63 @@ It is supposed to be implemented in the src file of the respective type.
4343function generate_element end
4444
4545
46+ # ##############################################################################
47+ #
48+ # `implements` trait
49+ #
50+ # ##############################################################################
51+
52+ # Calling `_implements(T, f)` checks whether a "sensible" method for the unary
53+ # function `f` is implemented for inputs of type `T`. The argument order is
54+ # meant to be similar to e.g. `isa`, and thus indicates `T implements f`.
55+ #
56+ # For example, `_implements(MyRingElem, is_unit)` should return true if
57+ # invoking `is_unit` on elements of type `MyRingElem` is supported.
58+ #
59+ # The generic fallback uses `hasmethod`. However, this may return `true` in
60+ # cases where it shouldn't, as we often provide generic methods for that rely
61+ # on other methods being implemented -- either for the same type, or for types
62+ # derived from it. For example the `is_nilpotent(::PolyElem{T})` method needs
63+ # `is_nilpotent(::T)` in order to work.
64+ #
65+ # To reflect this, additional `_implements` methods need to be provided.
66+ # We currently do this for at least the following functions:
67+ # - factor
68+ # - is_irreducible
69+ # - is_nilpotent
70+ # - is_squarefree
71+ # - is_unit
72+ # - is_zero_divisor
73+ #
74+ _implements (:: Type{T} , f:: Any ) where {T} = hasmethod (f, Tuple{T})
75+
76+ # Alternatively, the first argument can be a concrete object. By default we
77+ # then redispatch to the type based version. But one may also choose to
78+ # implement custom methods for this: certain operations will only work for
79+ # *some* instances. E.g. for `Z/nZ` it may happen that for `n` a prime we can
80+ # perform a certain operation, but not if `n` is composite.
81+ #
82+ # In that case the recommendation is that `_implements` invoked on the type
83+ # returns `false`, but invoked on a concrete instance of a type, it may use
84+ # specifics of the instance to also return `true` if appropriate.
85+ function _implements (x:: T , f:: Any ) where {T}
86+ @assert ! (x isa Type) # paranoia
87+ return _implements (T, f)
88+ end
89+
90+ # helper for `_implements` which checks if `f` has a method explicitly for
91+ # a concrete type `T` (i.e. not a generic method that can be specialized to `T`
92+ # but really one that is implement for `T` and `T` only).
93+ function _implements_directly (:: Type{T} , f:: Any ) where {T}
94+ isconcretetype (T) || return false # TODO : drop this?
95+ meth = methods (f, Tuple{T})
96+ # TODO : deal with type parameters: if `T` is `FreeAssociativeAlgebraElem{ZZRingElem}`
97+ # and `f` has a method for `FreeAssociativeAlgebraElem` then we should still consider
98+ # this a match.
99+ return any (m -> m. sig == Tuple{typeof (f), T}, meth)
100+ end
101+
102+
46103# ##############################################################################
47104#
48105# The following function stubs' actual implementations are in the folder `ext/TestExt/`.
0 commit comments