in a world where we have shadowing, what should this code do?
the relevant part is that when x = foo 1, y = foo 2, and z = foo 3 get run, they each happen after different bar values have been defined (or not), and foo calls bar
answer =
foo = \a -> bar a
x = foo 1
bar = \b -> foo b
y = foo 2
bar = \b -> foo (b + 42)
z = foo 3
I think a reasonable answer is "something should be disallowed here" - but if so, then what specifically should be disallowed?
for example, one plausible design is "pretend Roc is an interpreted language that allows reassignment, and then give errors at compile time for things that would break if this code was interpreted at runtime" - in which case:
x = foo 1 is a compile-time error, because if you were to run that in an interpreter, at that point bar would not be defined, so running foo wouldn't worky = foo 2 would be fine, and would compile to a call to foo that calls the first bar because that's what has been defined at this point (or would be at runtime)z = foo 3 would also be fine, except it would compile to a different specialization of foo which calls the second bar instead of the first one, because again that's what would happen in an interpreter (that is, the interpreter would reassign bar to something else, which would affect what foo did when it ran)however, this has the downside of now making it possible to need to create multiple specializations of a function based on shadowing
which isn't something we've talked about before, but which is something I suppose we could do
If you want to keep it simple, you could disallow shadowing lambdas
yeah and then only allow functions to capture things that have been defined earlier
Weren’t we going to require ordered defs anyway because of dbg?
the problem is that mutually recursive functions still have to be possible
Ah, right
another potential solution is to only allow mutually recursive functions at the top level
we had discussed that before somewhere, although I don't remember where :sweat_smile:
I guess that's the simplest design because the rule is basically "you have to define a value before you use it, except top-level values can be defined in any order and used in any order"
and I think maybe we also separately discussed not allowing top-level values to shadow each other, because ordering wouldn't be clear
That’s pretty clean if we can get away with it
I don’t remember writing mutually recursive functions inside a let in Elm
Last updated: Nov 08 2025 at 12:13 UTC