methodsjuliadispatch

What is the difference between defining a method with the `where` clause vs omitting it in Julia?


For example:

foo(x::Real) = x ^ 2
bar(x::R) where {R<:Real} = x ^ 2

Both seem to have similar performance, which seems to imply that the compiler is creating specialized code for, e.g., Int vs Float64. So is the only difference that in the latter, R can be used/referenced?


Solution

  • The two are the same:

    julia> foo(x::Real) = x ^ 2
    foo (generic function with 1 method)
    
    julia> bar(x::R) where {R <: Real} = x ^ 2
    bar (generic function with 1 method)
    
    julia> code_lowered
    code_lowered (generic function with 2 methods)
    
    julia> code_lowered(foo)
    1-element Vector{Core.CodeInfo}:
    CodeInfo(
    1 ─ %1 = Main.:^
    │   %2 = Core.apply_type(Base.Val, 2)
    │   %3 = (%2)()
    │   %4 = Base.literal_pow(%1, x, %3)
    └──      return %4
    )
    
    julia> code_lowered(bar)
    1-element Vector{Core.CodeInfo}:
    CodeInfo(
    1 ─ %1 = Main.:^
    │   %2 = Core.apply_type(Base.Val, 2)
    │   %3 = (%2)()
    │   %4 = Base.literal_pow(%1, x, %3)
    └──      return %4
    )
    

    The difference in fact is none in the code initially generated, even if you take out the restriction to Real:

    julia> baz(x) = x ^ 2
    baz (generic function with 1 method)
    
    julia> code_lowered(baz)
    1-element Vector{Core.CodeInfo}:
    CodeInfo(
    1 ─ %1 = Main.:^
    │   %2 = Core.apply_type(Base.Val, 2)
    │   %3 = (%2)()
    │   %4 = Base.literal_pow(%1, x, %3)
    └──      return %4
    )
    

    However, there will be a difference in the just-in-time compilation. If you supply a String argument, only baz("abc") will work -- the other two will not pass the JIT step because "abc" is not a Real.

    TLDNR: The difference in foo and bar is only in the syntax.