3 ms·
Edit: this was meant to be a reply to https://news.ycombinator.com/item?id=37122798 https://news.ycombinator.com/item?id=37122798 but I messed it up. I don't s
by sigwinch28 3y ago
Edit: this was meant to be a reply to https://news.ycombinator.com/item?id=37122798 https://news.ycombinator.com/item?id=37122798 but I messed it up.
I don't see a good reason Elixir wouldn't allow it, since Erlang (yes I know it's not Elixir) often compiles anonymous functions ("Funs") into top-level definitions...
Here's some IR showing how a top-level fun from this Erlang module...
-module(foo).
-export([f/1]).
f(X) ->
G = fun (Y) -> X + Y end,
G(X * 4) * G(X * 2).
...gets converted into a top-level function called '-f/1-fun-0-':
{module, foo}. %% version = 0
{exports, [{f,1},{module_info,0},{module_info,1}]}.
{attributes, []}.
{labels, 9}.
{function, f, 1, 2}.
[SNIPPED FOR HN]
{allocate,1,2}.
{move,{x,0},{y,0}}.
{swap,{x,0},{x,1}}.
{call,2,{f,8}}. % '-f/1-fun-0-'/2
{'%',{var_info,{x,0},[{type,number}]}}.
{gc_bif,'*',{f,0},1,[{tr,{y,0},number},{integer,2}],{x,1}}.
{move,{y,0},{x,2}}.
{move,{x,0},{y,0}}.
{move,{x,1},{x,0}}.
{move,{x,2},{x,1}}.
{call,2,{f,8}}. % '-f/1-fun-0-'/2
{'%',{var_info,{x,0},[{type,number}]}}.
{gc_bif,'*',{f,0},1,[{tr,{y,0},number},{tr,{x,0},number}],{x,0}}.
{deallocate,1}.
return.
{function, '-f/1-fun-0-', 2, 8}.
{label,7}.
{line,[{location,"foo.erl",5}]}.
{func_info,{atom,foo},{atom,'-f/1-fun-0-'},2}.
{label,8}.
{gc_bif,'+',{f,0},2,[{x,1},{x,0}],{x,0}}.
return.
- throwawaymaths 3y agoIf you stripped the arity how would you know how many register need to be moved into the new call routine's register list, so that reordering, clobbering, etc. at the call site are optimized?
- sigwinch28 3y agoI am a bit rusty on the specifics of the BEAM's registers (the VM that Erlang and Elixir run on) but IIRC the short version is these are registers in a VM and the VM takes care not to let them clobber. Also, in the BEAM, intra-module and inter-module calls operate differently. The Erlang/OTP did a blog post on this a little while ago which I think is great: https://www.erlang.org/blog/a-brief-beam-primer/ https://www.erlang.org/blog/a-brief-beam-primer/ Here is some useful info: > BEAM is a register machine, where all instructions operate on named registers. Each register can contain any Erlang term such as an integer or a tuple, and it helps to think of them as simple variables. The two most important kinds of registers are: > * X: these are used for temporary data and passing data between functions. They don’t require a stack frame and can be freely used in any function, but there are certain limitations which we’ll expand on later. > * Y: these are local to each stack frame and have no special limitations beyond needing a stack frame.
- throwawaymaths 3y ago> VM takes care not to let them clobber. The Erlang VM absolutely clobbers registers within functions all the time, since there are only 64? of them and they are a 'limited' resource. My point is that the logic to efficiently move data around is more complicated when you don't know the arity ahead of time. You can't, for example, keep a register file as a linear slice. You have to allocate a whole slate of 64 registers on each call with the last (n) registers blanked.
- sigwinch28 3y agoYes, you’re right.