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