@@ -142,16 +142,42 @@ end
142142# dx::AbstractArray (when both are possible), or the reverse. So for now we just pass them through:
143143(:: ProjectTo{T} )(dx:: Tangent{<:T} ) where {T} = dx
144144
145+ # ####
146+ # #### A related utility which wants to live nearby
147+ # ####
148+
149+ """
150+ is_non_differentiable(x) == is_non_differentiable(typeof(x))
151+
152+ Returns `true` if `x` is known from its type not to have derivatives, else `false`.
153+
154+ Should mostly agree with whether `ProjectTo(x)` maps to `AbstractZero`,
155+ which is what the fallback method checks. The exception is that it will not look
156+ inside abstractly typed containers like `x = Any[true, false]`.
157+ """
158+ is_non_differentiable(x) = is_non_differentiable(typeof(x))
159+
160+ is_non_differentiable(:: Type{<:Number} ) = false
161+ is_non_differentiable(:: Type{<:NTuple{N,T}} ) where {N,T} = is_non_differentiable(T)
162+ is_non_differentiable(:: Type{<:AbstractArray{T}} ) where {T} = is_non_differentiable(T)
163+
164+ function is_non_differentiable(:: Type{T} ) where {T} # fallback
165+ PT = Base. _return_type(ProjectTo, Tuple{T}) # might be Union{} if unstable
166+ return isconcretetype(PT) && PT <: ProjectTo{<:AbstractZero}
167+ end
168+
145169# ####
146170# #### `Base`
147171# ####
148172
149173# Bool
150174ProjectTo(:: Bool ) = ProjectTo{NoTangent}() # same projector as ProjectTo(::AbstractZero) above
175+ is_non_differentiable(:: Type{Bool} ) = true
151176
152177# Other never-differentiable types
153178for T in (:Symbol, :Char, :AbstractString, :RoundingMode, :IndexStyle)
154179 @eval ProjectTo(:: $T ) = ProjectTo{NoTangent}()
180+ @eval is_non_differentiable(:: Type{<:$T} ) = true
155181end
156182
157183# Numbers
0 commit comments