@@ -43,6 +43,63 @@ It is supposed to be implemented in the src file of the respective type.
43
43
function generate_element end
44
44
45
45
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
+
46
103
# ##############################################################################
47
104
#
48
105
# The following function stubs' actual implementations are in the folder `ext/TestExt/`.
0 commit comments