Skip to content

Conversation

@murisi
Copy link
Collaborator

@murisi murisi commented Nov 20, 2025

Added a test for lexical scoping and a way that it could be theoretically implemented. The test is equivalent to the following Scheme code: (display (((lambda (x) (lambda () x)) 3141592))) (which should display 3141592). Running the test on the current branch errors out when it's unable to find the variable x.

But the above approach (which is also in this PR) to adding lexical scoping might be undesirable because it means that some functions in the Scheme registry can no longer be defined using only the DSL (using a {"closure", params, body} triple). Instead they would have to be defined using Elixir functions/closures (i.e. {"closure", fn -> ... end}).

Another approach to implementing Scheme closures in a way that passes the above test could be to add a third field to the "closure" structure to also store env (at the point when the lambda was evaluated) in. In this case, the body of the closure would be evaluated in an environment formed by taking the environment stored in the closure (instead of the environment at the point of application) and adding bindings from the closure's parameters to the closure's arguments.

But this second approach might be problematic when defining some of the functions in the Scheme registry because it would then require that there be a way to express recursive bindings in the DSL itself (since the binding nth does not exist during its definition). Something like a letrec or define construct that allows a definition to refer to itself. In the compiler, I essentially took the latter approach but with define renamed to function.

Another idea that comes to mind: if the functions that are currently defined (using the DSL) in the environment could somehow be moved/prepended to the actual DSL program, then it might make it easier to compile everything. The Scheme compiler would then simply be able to take an expression (or list of them) containing the actual program and definitions of map, take, etc, and convert them to C without needing to process an environment. For example, like:

[[:function, :filter, [:xs, :f],
[:if, [:is_null, :xs],
[:null],
[:if, [:f, [:car, :xs]],
[:cons, [:car, :xs], [:filter, [:cdr, :xs], :f]],
[:filter, [:cdr, :xs], :f]]]],
[:function, :scm_main, [],
[[:function, :_, [:n],
[:commit_sexpr, [:filter, [:read_sexpr],
[:function, :divisible_by_n, [:x], [:==, [:%, [:as_integer, :x], :n], 0]]]]], 3]]])
.

@murisi murisi marked this pull request as draft November 20, 2025 15:04
@murisi murisi requested a review from l4e21 November 20, 2025 15:47
@l4e21 l4e21 force-pushed the jam/feature/elixir-resource-machine branch 5 times, most recently from 2dc0b4c to 60c1de0 Compare November 21, 2025 17:19
@l4e21 l4e21 force-pushed the jam+murisi/feature/elixir-resource-machine+lexical+scoping branch 2 times, most recently from 77cdb8f to d3907d3 Compare November 24, 2025 12:43
@l4e21 l4e21 force-pushed the jam/feature/elixir-resource-machine branch 2 times, most recently from 123af59 to 60c1de0 Compare November 24, 2025 12:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants