# Dispatching on function types?

Motivating problem:

``````julia> struct MappedElement{F <: Function,T <: Real} <: Real
x::T
fx::T
MappedElement{F}(x::T, fx::T) where {F,T} = new{F,T}(x, fx)
MappedElement{F}(x, fx) where {F} = MappedElement{F}(promote(x, fx)...)
end

julia> MappedElement(f::F, x) where {F} = MappedElement{F}(x, f(x))
MappedElement

julia> (::typeof(F))(x::MappedElement{typeof(F)}) where {F} = x.fx # errors
ERROR: TypeError: in MappedElement, in F, expected F<:Function, got Type{TypeVar}
Stacktrace:
[1] top-level scope at REPL[3]:1

julia> (::typeof(F))(x::MappedElement{F}) where {F} = x.fx # does not error

julia> meexp = MappedElement(exp, 2.3);

julia> exp(meexp) # errors
ERROR: MethodError: no method matching exp(::MappedElement{typeof(exp),Float64})
Closest candidates are:
exp(::BigFloat) at mpfr.jl:618
exp(::Missing) at math.jl:1138
exp(::Complex{Float16}) at math.jl:1086
...
Stacktrace:
[1] top-level scope at REPL[6]:1

julia> (::typeof(log))(x::MappedElement{typeof(log)}) = x.fx

julia> melog = MappedElement(log, 2.3); # works

julia> log(melog) #works
0.8329091229351039
``````

The idea would be to have a number that caches the result of some function applied to it.

The definition:

`````` (::typeof(F))(x::MappedElement{F}) where {F} = x.fx # does not error
``````

looks wrong, and also fails to actually work, but evaluating it does not error, unlike

`````` (::typeof(F))(x::MappedElement{typeof(F)}) where {F} = x.fx
``````

For what it’s worth, lowered forms of the expressions:

``````julia> Meta.@lower (::typeof(log))(x::MappedElement{typeof(log)}) = x.fx
:(\$(Expr(:thunk, CodeInfo(
@ none within `top-level scope'
1 ─ %1 = typeof(log)
│   %2 = typeof(log)
│   %3 = Core.apply_type(MappedElement, %2)
│   %4 = Core.svec(%1, %3)
│   %5 = Core.svec()
│   %6 = Core.svec(%4, %5)
│        \$(Expr(:method, false, :(%6), CodeInfo(quote
Base.getproperty(x, :fx)
return %1
end)))
└──      return
))))

julia> Meta.@lower (::typeof(F))(x::MappedElement{::typeof(F)}) where {F} = x.fx
:(\$(Expr(:error, "invalid \"::\" syntax")))

julia> Meta.@lower (::typeof(F))(x::MappedElement{typeof(F)}) where {F} = x.fx
:(\$(Expr(:thunk, CodeInfo(
@ none within `top-level scope'
1 ─ %1 = Core.TypeVar(:F)
│   %2 = typeof(%1)
│   %3 = typeof(%1)
│   %4 = Core.apply_type(MappedElement, %3)
│   %5 = Core.svec(%2, %4)
│   %6 = Core.svec(%1)
│   %7 = Core.svec(%5, %6)
│        \$(Expr(:method, false, :(%7), CodeInfo(quote
Base.getproperty(x, :fx)
return %1
end)))
└──      return
))))

julia> (::typeof(F))(x::MappedElement{typeof(F)}) where {F} = x.fx
ERROR: TypeError: in MappedElement, in F, expected F<:Function, got Type{TypeVar}
Stacktrace:
[1] top-level scope at REPL[19]:1

julia> Meta.@lower (::typeof(F))(x::MappedElement{F}) where {F} = x.fx
:(\$(Expr(:thunk, CodeInfo(
@ none within `top-level scope'
1 ─ %1 = Core.TypeVar(:F)
│   %2 = typeof(%1)
│   %3 = Core.apply_type(MappedElement, %1)
│   %4 = Core.svec(%2, %3)
│   %5 = Core.svec(%1)
│   %6 = Core.svec(%4, %5)
│        \$(Expr(:method, false, :(%6), CodeInfo(quote
Base.getproperty(x, :fx)
return %1
end)))
└──      return
))))
``````

In the case where I write `::MappedElement{typeof(F}}`, the code contains

``````1 ─ %1 = Core.TypeVar(:F)
│   %3 = typeof(%1)
│   %4 = Core.apply_type(MappedElement, %3)
``````

Removing the `typeof` eliminates the `%3`. The non-parameteric `typeof(log)` version directly `Core.apply_type`s using `typeof(log)`.

To overload calls to functions passed a function, I cannot use `typeof`:
`Base.broadcasted(::F, x::MappedElement{F}) where {F} = x.fx`
But this does not work for defining a call:

``````julia> (::F)(x::MappedElement{F}) where {F} = x.fx
ERROR: function type in method definition is not a type
Stacktrace:
[1] top-level scope at REPL[27]:1
``````

Any ideas?

The concrete version of this works:

``````meexp = MappedElement(exp, 2.3);
Base.exp(x::MappedElement{typeof(exp)}) = x.fx
exp(meexp) # works
``````

so I think this is a bug (or missing feature). I would recommend opening an issue.