Type instability in code with branches and QuadGK uses

I have a code which calculates complicated integral and which one should be used depends on the parameters. The code is chronically type-unstable (union with Core.Box appears) which makes it few times slower than it should be. The MWE is like this

using QuadGK

function f1(x,b)
    x < 0 && ( b = -b )
    I, _err = quadgk(t->exp(-t*b),0,1)
    return I
end

You can also try b = sign(x)*b, result is the same. In this case I can fix it by switching to

function f2(x,b)
    I, _err = if x < 0
         quadgk(t->exp(-t*b),0,1)
    else
        quadgk(t->exp(t*b),0,1)
    end
    return I
end

But it is hard to understand why this behaviour occurs, and in the full code I have multiple conditions to deal with.

Reassigning a captured variable like b at any point currently makes the lowerer implement it as a Core.Box field in the closure’s type. In your fix, you don’t reassign b, you just switch between different closures. Alternatively, you could capture a new local variable b2 = if x < 0; -b else b end or the branchless b2 = ifelse(x<0, -b, b).

The code

function f3(x,b)
   b2 = ifelse(x < 0, -b, b)
   I, _err = quadgk(t->exp(-t*b2),0,1)
   return I
end

is still type-unstable.

I think this is the usual problem with captured mutated variables in closures (julia#15276). A simple workaround is to re-assign b to a local variable in a let block before capturing it:

I, _err = let b=b; quadgk(t->exp(-t*b),0,1); end

Another option is to use function composition and Base.Fix:

function f3(x,b)
     x < 0 && ( b = -b )
     I, _err = quadgk(exp∘Base.Fix1(*,-b),0,1)
     return I
 end