passerine icon indicating copy to clipboard operation
passerine copied to clipboard

Document hoisting semantics

Open mkhan45 opened this issue 4 years ago • 1 comments

Right now, this example prints 106:

z = 3
f = x -> x + y + z
y = 5
z = 100

println (f 1)  --  106

z is marked as resolved and f captures its reference, so it later evaluates to 100 when f is called. y is properly hoisted such that it evaluates to 5 in the closure.

Instead, this should be a compile time failure since z is marked as immutable by f's capture.

Discord thread: https://discordapp.com/channels/651996477333438474/730624907607539722/904815689154564227

mkhan45 avatar Nov 02 '21 23:11 mkhan45

Thanks for taking the time to put this in! Aside from hoisting, we need to document the semantics of mutability and immutability in Passerine, especially the concept of 'local mutability.' Maybe now would be a good time to start building out the Codex?

Here's a brief rundown of what should happen:

  1. Variables are mutable in the scope in which they are defined, i.e. while on the stack.
    -- this is valid
    x = 5
    x = 4
    
  2. Function calls are pass-by-value. Mutating a function parameter makes a copy[^1] and does not effect the original value:
    f = x -> { x = 2; x }
    y = 7
    f y -- is 2
    y -- is 7
    
  3. Closures are capture-by-value. Mutating a captured value (inside or outside the closure) is a compile-time error:
    -- compile-time error:
    x = 7
    f = () -> { x = 2; x }
    -- also:
    x = 7
    f = () -> x
    x = 2
    
  4. Variables may be shadowed:
    x = 7
    f = x -> { x = 2; x } -- shadowed
    f 7 -- is 2
    x -- is 7
    

This is just a brief overview, I think we should provide more examples in the documentation. I'd like to include a method for closures to capture by reference in trivial cases, i.e. closures that do not escape the scope in which they are defined. I'd also like a way to introduce variables guaranteed to be immutable, through something like let x = ....

[1]: Pass-by-value does not always cause an actual copy to occur because of FBiP due to Vaporization. In the case of last-use, the original value is passed and may be mutated:

mutate_first = list -> { list[0] = 0; list }

big_list = [ ... ]
big_list = mutate_first big_list -- last use of big_list
print big_list -- no copies have been made

slightknack avatar Nov 03 '21 06:11 slightknack