It's not a huge issue other than compatibility with Clojure, but it pollutes the global scope so one must be careful to name it something sufficiently unique.
The solution is to make it behave like let*, which creates a new environment that "goes away" once it's out of scope:
case "let*":
var let_env = new Env(env);
for (var i = 0; i < a1.length; i += 2) {
let_env.set(a1[i], EVAL(a1[i + 1], let_env));
}
ast = a2;
env = let_env;
break;
All it does is return a function which has been passed the current environment, and the only logic is checking whether the second argument is a list which is assumed to be the first of one or more arity bodies and if so it is handled by a special multifn type.
I don't believe it's possible for a local function (not a defn) to both have a name and multiple arities. Even the latter case is very rare, only used in a handful of higher order functions like juxt.
Here is the types._function type:
export function _function(Eval, Env, ast, env, params) {
var fn = function () {
return Eval(ast, new Env(env, params, arguments));
};
fn.__meta__ = null;
fn.__ast__ = ast;
fn.__gen_env__ = function (args) { return new Env(env, params, args); };
fn._ismacro_ = false;
return fn;
}
As we can see the function is already evaluated (only when called) in a fresh environment so that's not the issue. And I suppose this isn't even the place to add naming logic. I think what we need is another form of def that behaves more like let. Here's the def special form:
case "def":
var res = EVAL(a2, env);
env.set(a1, res);
return res
Very simple. It defines the symbol in the current env. So we could have something similar in fn that runs only in the case where the second arg is a symbol, and is given a new env instead, and sets the current env as the new one.
The only other thing is that in defn, the function is also given :name metadata so that other parts of the code can be aware of it, like the printer. So I imagine it could return a function like
var fn_env = new Env(env)
with_meta(types._function(types._function(EVAL, Env, ast.slice(2), fn_env, a2), {name: a1})
env = fn_env
There is currently not the option of giving a local function a name. As a result there are certain places where a
defn
must be used inside another function because it is too intertwined with the rest of it to be conveniently broken out into a helper function, and must be able to call itself (seefor
): https://github.com/bobbicodes/bien/blob/18849ea7708176d15ab13be748c0c8de334c9af8/src/clj/core.clj#L631Ordinarily this would be a named lambda, i.e.
It's not a huge issue other than compatibility with Clojure, but it pollutes the global scope so one must be careful to name it something sufficiently unique.
The solution is to make it behave like
let*
, which creates a new environment that "goes away" once it's out of scope:This is the current
fn
special form:All it does is return a function which has been passed the current environment, and the only logic is checking whether the second argument is a list which is assumed to be the first of one or more arity bodies and if so it is handled by a special
multifn
type.I don't believe it's possible for a local function (not a
defn
) to both have a name and multiple arities. Even the latter case is very rare, only used in a handful of higher order functions likejuxt
.Here is the
types._function
type:As we can see the function is already evaluated (only when called) in a fresh environment so that's not the issue. And I suppose this isn't even the place to add naming logic. I think what we need is another form of
def
that behaves more likelet
. Here's thedef
special form:Very simple. It defines the symbol in the current env. So we could have something similar in
fn
that runs only in the case where the second arg is a symbol, and is given a new env instead, and sets the current env as the new one.The only other thing is that in
defn
, the function is also given:name
metadata so that other parts of the code can be aware of it, like the printer. So I imagine it could return a function like