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: Jul 06 2025 at 12:14 UTC