@@ -452,3 +452,59 @@ function _number_of_direct_product_factors end
452452Return the homomorphism from the domain `D` into the codomain `C` defined by the data.
453453"""
454454function hom end
455+
456+ # ##############################################################################
457+ #
458+ #
459+ #
460+ # ##############################################################################
461+
462+ # Calling `_implements(T, f)` checks whether a "sensible" method for the unary
463+ # function `f` is implemented for inputs of type `T`. The argument order is
464+ # meant to be similar to e.g. `isa`, and thus indicates `T implements f`.
465+ #
466+ # For example, `_implements(MyRingElem, is_unit)` should return true if
467+ # invoking `is_unit` on elements of type `MyRingElem` is supported.
468+ #
469+ # The generic fallback uses `hasmethod`. However, this may return `true` in
470+ # cases where it shouldn't, as we often provide generic methods for that rely
471+ # on other methods being implemented -- either for the same type, or for types
472+ # derived from it. For example the `is_nilpotent(::PolyElem{T})` method needs
473+ # `is_nilpotent(::T)` in order to work.
474+ #
475+ # To reflect this, additional `_implements` methods need to be provided.
476+ # We currently do this for at least the following functions:
477+ # - factor
478+ # - is_irreducible
479+ # - is_nilpotent
480+ # - is_squarefree
481+ # - is_unit
482+ # - is_zero_divisor
483+ #
484+ _implements (:: Type{T} , f:: Any ) where {T} = hasmethod (f, Tuple{T})
485+
486+ # Alternatively, the first argument can be a concrete object. By default we
487+ # then redispatch to the type based version. But one may also choose to
488+ # implement custom methods for this: certain operations will only work for
489+ # *some* instances. E.g. for `Z/nZ` it may happen that for `n` a prime we can
490+ # perform a certain operation, but not if `n` is composite.
491+ #
492+ # In that case the recommendation is that `_implements` invoked on the type
493+ # returns `false`, but invoked on a concrete instance of a type, it may use
494+ # specifics of the instance to also return `true` if appropriate.
495+ function _implements (x:: T , f:: Any ) where {T}
496+ @assert ! (x isa Type) # paranoia
497+ return _implements (T, f)
498+ end
499+
500+ # helper for `_implements` which checks if `f` has a method explicitly for
501+ # a concrete type `T` (i.e. not a generic method that can be specialized to `T`
502+ # but really one that is implement for `T` and `T` only).
503+ function _implements_directly (:: Type{T} , f:: Any ) where {T}
504+ isconcretetype (T) || return false # TODO : drop this?
505+ meth = methods (f, Tuple{T})
506+ # TODO : deal with type parameters: if `T` is `FreeAssociativeAlgebraElem{ZZRingElem}`
507+ # and `f` has a method for `FreeAssociativeAlgebraElem` then we should still consider
508+ # this a match.
509+ return any (m -> m. sig == Tuple{typeof (f), T}, meth)
510+ end
0 commit comments