Another annoying compiler question. I'm trying to figure out the rationale behind this small section of code in Compiler.eval:
else if((form instanceof IType) ||
(form instanceof IPersistentCollection
&& !(RT.first(form) instanceof Symbol
&& ((Symbol) RT.first(form)).name.startsWith("def"))))
{
ObjExpr fexpr = (ObjExpr) analyze(C.EXPRESSION, RT.list(FN, PersistentVector.EMPTY, form),
"eval" + RT.nextID());
IFn fn = (IFn) fexpr.eval();
return fn.invoke();
}
else
{
Expr expr = analyze(C.EVAL, form);
return expr.eval();
}
One can try to analyze this from several standpoints: (a) Why wrap some forms in an fn* and analyze in mode C.Expression? (b) Why not wrap all forms in this way?
Is the answer to (b) "it's too inefficient to wrap everything"?
Is the answer to (a) "some Expr types throw an Exception on expr.eval" ?
If that is the correct answer to (a), it raises the follow-on question of "why do those Expr types throw exceptions when you try to eval them?" There are twelve offenders: Let, LetFn, Try, Case, Recur, Throw, StaticInvoke, MonitorEnter, MonitorExit, UnresolvedVar, MethodParam, LocalBinding.
• It appears Let, LetFn, and Try are not really problems; if they are analyzed in a C.Eval context, they wrap themselves in an fn* and are happy.
• Why can't Case do the same as Let, LetFn, Try?
• Recur doesn't really matter -- it is going to cause a problem if encountered outside of loop context, so it should be covered by other error cases.
• StaticInvoke: we can handle other method invocations in eval mode? Why is StaticInvoke different?
• MonitorEnter, MonitorExit: I would have not trouble handling evals of these on the CLR. Is there a problem on the JVM? Could you not just call some little static method to do the job?
• UnresolvedVar -- the 'emit' is to do nothing. I don't see a problem with either 'eval' doing nothing or throwing an exception.
• MethodParam -- can't be emitted either -- it should never be encountered during evaluation.
• LocalBinding -- will not be encountered outside of a binding scope, meaning you are already in an fn*
• Throw -- you can certainly call throw from ThrowExpr.eval() -- is the stacktrace going to be that much more misleading?
Last question: Why does an IType value need to be wrapped in an fn* ?
That's a lot of mystery wrapped in few lines of code. 😞