In Julia, how to handle the method correctly, arguments of (super-) types that are also provided by the caller?

I would like to define the function f(x, t::Type) , which performs a different behavior depending on whether, isa(x, t) . Let's say that I want to call b1(x) , if so, and b2(x) otherwise.

I know that I can perform a dynamic check at runtime as follows:

 function f(x, t::Type) if isa(x, t) b1(x) else b2(x) end end 

However, is there a way to do this exclusively with the parametric send method? For example, if I define

 f{T}(x::T, t::Type{T}) = b1(x) f(x, t::Type) = b2(x) 

for f(1, Int) and f(1.0, Int) correct behavior is invoked. But I want this to work for all t subtypes as well:

 f(1, Number) 

This actually calls b2 because the first signature of f does not match. Interestingly, however, f(x::Number, t::Type{Number}) = b1(x) will match in this case.

Did I miss something obvious here?

This was a bug, and it was fixed in 0.4.


Questions:

  • Why does f{T}(x::T, t::Type{T}) not match for f(1, Number) , although there is a type replacement for t ( Number ) that will match?

  • Using f{T2, T1 <: T2}(x::T1, t::Type{T2}) or something similar does not work, because all static parameters enter the scope only after closing the full list of static parameters. Why?

  • Are there any penalties for the effectiveness of using a dynamic approach?

  • How to define methods as an internal function, so I can bind t to a local variable, for example: function f(x, t::Type); g(x::t) = b1(x); g(x) = b2(x); g(x) end function f(x, t::Type); g(x::t) = b1(x); g(x) = b2(x); g(x) end

    It works, but what are the costs of performance?

  • What is the idiomatic / preferred way to solve this problem?

(I tried this on 0.3.2.)

+5
source share
1 answer

To answer your questions:

  • AFAICT is a must. As user3580870 commented, this seems to work in Julia 0.4.
  • This is a "triangular dispatch" and has not yet been implemented. See # 3766 .
  • It depends. The compiler can estimate isa(x, t) at compile time and resolve branch. However, there are several more possible ways to use isa : suboptimal:
    • If two branches return different types, type inference will not be able to correctly output the return type, because this happens before isa(x, t) turns into a constant. (This is likely to be costly, and other possible deviations below are probably not a big deal.)
    • If one branch distributes the heap and the other does not, an unnecessary GC frame may occur.
    • Function cannot be built in.
  • An internal function will have the same performance problems as when using isa or more. As in Julia 0.5, the inner function should be as efficient as the top-level function in this case.
  • File with an error ;-). I think this should work. Perhaps any correction of this at 0.4 can be passed up to 0.3. But at the same time, isa not bad, provided that it does not cause a type inference problem.
+5
source

Source: https://habr.com/ru/post/1206572/


All Articles