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?