JuliaLang / julia

The Julia Programming Language
https://julialang.org/
MIT License
45.32k stars 5.45k forks source link

stackoverflow anonymous function #37868

Open maartenvd opened 3 years ago

maartenvd commented 3 years ago
julia> function fun(a)
           a::typeof(a) = a
           function helper(b)
               a=b+a
           end

           helper(3.5)
           a
       end
ERROR: StackOverflowError:

julia> versioninfo()
Julia Version 1.5.1
Commit 697e782ab8 (2020-08-25 20:08 UTC)
Platform Info:
  OS: Linux (x86_64-linux-gnu)
  CPU: Intel(R) Core(TM) i7-8750H CPU @ 2.20GHz
  WORD_SIZE: 64
  LIBM: libopenlibm
  LLVM: libLLVM-9.0.1 (ORCJIT, skylake)
Environment:
  JULIA_NUM_THREADS = 2

While there are a number of bug reports open about stackoverflows when defining functions (so this issue is possibly a duplicate), the examples looked quite different : #36185 , #36804,#36544

The issue I'm actually trying to solve is that when inference fails, it causes really significant startup overhead (possibly because the packages I'm using are template heavy ?). I often lose type stability because I use anonymous functions, which depend on variables outside their scope (so like in the example). This is really annoying, and the only workaround I found so far was to sprinkle let blocks everywhere. Of course - ideally - the workaround shouldn't be necessary and julia would simply stop failing to infer those things. I was trying out the following macro as a workaround, which would then fix the types of the variables it get's passed to:

macro type_stabilize(var)
          if isa(var,Symbol)
              return esc(:($var::typeof($var)=$var))
          else
              (isa(var,Expr) && var.head == :tuple) || throw(ArgumentError("bad format"))
              expr = :()
              for symbol in var.args
                  expr = :($expr; $symbol::typeof($symbol) = $symbol);
              end
              return esc(expr)
          end
       end

If anyone knows another way to get this to work, I would be very grateful.

KristofferC commented 3 years ago

If anyone knows another way to get this to work, I would be very grateful.

You could take a look at https://github.com/c42f/FastClosures.jl.

maartenvd commented 3 years ago

I tried it out, but the allowed syntax is quite limited. For example, I usually write things like


eigsolve(x,1,:LM) do v
#some multiline function that applies a linear map
end

This is annoying to rewrite to use fastclosures, I think I would have to do things like


@closure function apply_map(v)
#transform v
end
eigsolve(apply_map,x,1;LM)

I also had problems where type stability was still incorrectly lost, and it will never be able to correct for the case where the closure changes a variable outside it's scope, but keeps the type.

EDIT : I was wrong about the syntax, fastclosures supports do blocks

JeffBezanson commented 3 years ago

A good workaround for now is:

function f(a::T) where T
    a::T = a
    ...