Conditional functions within functions


#1

consider the following code:

function foo(x::Int; d=:out)
    myfn = (d == :out) ? fn1 : fn2
    z = 0
    for i = 1:x 
        z += myfn(i)
    end
end

I understand the inefficiency that results from the compiler not being able to tell that myfn won’t change within the loop. Is there a way to do this efficiently that preserves the simplicity of the function? I tried wrapping the loop inside a let myfn = (d == :out) ? fn1 : fn2 block but that didn’t show any benchmarking improvements.


#2

Try using a barrier function?


#3

@mauro3 - yeah, @ChrisRackauckas suggested that as well but it violates the “preserves the simplicity” goal.


#4

https://github.com/JuliaLang/julia/issues/21925


#5

I believe the usual solution is to use functions as values:

function foo(x::Int, myfn=fn1)
    z = 0
    for i = 1:x 
        z += myfn(i)
    end
    return z
end

#6

does this work if myfn is a keyword argument?


#7

I would assume not because the keyword argument would not be specialized on. You could write two copies of the function: one with positional arguments, and the other with keyword arguments.


#8

I think I’m going to go with this:

foo(x::Int; d=:out) = (d == :out) ? _foo(x, fn1) : _foo(x, fn2)
function _foo(x::Int, myfn::Function)
  ...
end

#9

Who said that?


#10

I think it’s true (that kwargs aren’t specialized on):

julia> foo(x::Int; d::Float64=2.0) = 1
foo (generic function with 1 method)

julia> foo(x::Int; d::Int=2) = 2
foo (generic function with 1 method)

julia> foo(2)
2

#11

What’s true? You are just showing that you cannot dispatch on keyword argument. It has nothing to do with specialization.


#12

Then I guess I’m confused as to what specialization is (as opposed to dispatch). Never mind.


#13

You’re right; I was a little sloppy with my language. The types of keyword arguments are not specialized on, which is what is important for this question.


#14

Then language you use does mean that but it’s not the case (i.e. what you are claiming is wrong). If you read it somewhere in the doc then the doc must be corrected.


#15

I think that I am confused too. @yuyichao, would you mind defining those two terms for me?


#16

Dispatch means picking a method, specialize means optimizing/compiling a method/function for a given (set of) input type(s).


#17

In fact, the document explicitly mentioned that the keyword arguments are being specialized on:

Functions are specialized on the types of keyword arguments


#18

But only one method/function is compiled in my example above (see the “generic function with 1 method” output after the second line), even though the kwarg has a different type. This implies that the type of the kwarg is ignored when determining the set of input types, right?

julia> foo(2; d=3.3)
ERROR: TypeError: #foo: in typeassert, expected Int64, got Float64

#19

No, only one method is added to the function, which is unrelated to the number of specilizations. FWIW, your _foo also has only one method.

No, it implies that the keyword are being ignored for dispatch (i.e. picking method), which, again, has nothing to do with specialization.


#20

We’re way off topic here, I think, and I’m utterly confused, so I’ll bow out at this point. Thanks.