alpaca-lang / alpaca

Functional programming inspired by ML for the Erlang VM
Other
1.44k stars 47 forks source link

Zero arity functions #238

Open lpil opened 6 years ago

lpil commented 6 years ago

Currently it's not possible to define a zero arity function in Alpaca as the compiler treats them as values. I can see a few use cases for zero arity functions.

Side effect free beam interop

There are some pure zero Erlang functions such as sets:new(). These are effectively values (the result is the same on ever invocation) though we need to call the function to pull out the value. If we wrap this value in Alpaca we need to wrap it in a > 0 arity function and discard the argument as it's not possible to use beam to define a value.

Functions with side causes

Some 0 arity functions in Erlang return a different value each time they are called, i.e. rand:uniform/0 and erlang:make_ref/0.

Functions with side effects

Functions that take no arguments and dispatch a side effect can be useful. Examples would include functions that start processes (my_actor:start_link/0) and callback functions passed into behaviour modules (CleanupJob())

j14159 commented 6 years ago

I can't recall why we didn't support 0-arity functions in the beginning and it's entirely possible that it's a position I took without thinking all the way through the consequences. I'm pretty sure this came up when @lepoetemaudit added top-level value bindings (current 0-arity implementation) so would like to leave this open for general discussion.

Full disclosure: I kind of like having separate "pure" value bindings at the top level and don't think it's too bad for us to do things like:

let startSomething () beam :someMod :someFun [] with x -> x

But that's not a hard line for me and I could be convinced otherwise.

lpil commented 6 years ago

I quite like the split between pure values and 0 arity functions

lepoetemaudit commented 6 years ago

I've hit this quite a lot myself while playing around.

There is an explicit guard in the code against zero arity functions because otherwise you could have side effects merely by mentioning a name of the function, which could lead to very hard to reason about code... In OCaml, for example, you can't have zero arity functions, because they become values at runtime (but before you call them!), so passing unit () is a really common pattern whenever you want to 'defer' something.

However, there are cases where it can get quite annoying, e.g. we can't curry in a value, so something like this won't compile:

let element name attrs nodes = (name, attrs, nodes)
let h1 = element "h1"
let p = element "p"

Unfortunately it would be difficult for values to work as in OCaml as Erlang has no concept of runtime values unless you fake it with processes (or hack module dictionaries / use ETS, perhaps).

Personally I'd be happy if the restriction was removed, but would prefer library functions that issue side effects to take () .

lpil commented 6 years ago

I presumed that a zero sorry arity function would be one that takes unit () as an argument, but compiles to a regular Erlang zero arity function to make interop easier.

lepoetemaudit commented 6 years ago

Right, I think I parsed the original issue wrong. That makes sense. It would still be nice if we can have potentially 'impure' values though, e.g.:

let add x y = x + y

let addTen =
  let amount = 10 in
  add amount

That's contrived, but it would be disallowed as being 'potentially impure' by Alpaca right now, even though it isn't at all.