I have two functions f(x, y)
and g(x, y)
. For some types of x
, it may be advantageous to reuse components of calculations when both are needed, for some other types this is not the case. On the other hand, sometimes I just need f
or g
.
I could define
fg(x, y) = f(x,y), g(x, y)
f(x, y) = fg(x,y)[1]
g(x, y) = fg(x,y)[2]
as fallback methods, and then either define
fg(x::SomeType, y) = ...
or
f(x::SomeOtherType, y) = ...
g(x::SomeOtherType, y) = ...
The problem is that if I have a missing method, I get a StackOverflowError
. Is there a way to avoid this, and break the circularity when I don’t have the right methods?
I’ve been battling this a lot recently, I hope you can get a good way for handling that!
Liso
January 7, 2018, 7:56pm
3
What about this trick?
fg(x, y, undef=false) = if undef "stack","overflow" else f(x,y), g(x, y) end;
f(x, y, undef=true) = fg(x,y,undef)[1]
g(x, y, undef=true) = fg(x,y,undef)[2]
You could overwrite without undef
argument:
f(x::Int, y) = 0
g(x::Int, y) = 0
fg(1,1) # -> (0, 0)
fg(1.,1) # -> ("stack", "overflow")
1 Like
This works, but AFAICT has a costly method table lookup. Can this be optimized away somehow?
@inline function fallback(fun, x::T) where T
if which(fun, Tuple{T}) ≡ which(fun, Tuple{Any})
error("no fallback method `$fun` for `$T`")
else
fun(x)
end
end
f(x) = fallback(fg, x)[1]
g(x) = fallback(fg, x)[2]
fg(x) = fallback(f, x), fallback(g, x)
f(x::Float64) = x + 1
g(x::Float64) = zero(x)
fg(x::Int) = x^2, 2*x
The costly method lookup is just from type instability. Change it to
function fg(x, y, undef=false)
undef && error("You must define either `fg` or `f` and/or `g` for inputs ", x, " and ", y)
f(x, y), g(x, y)
end
and the performance hit should go away.
1 Like