[racket] Use of map and eval to evaluate symbol in namespace

From: Matthew Flatt (mflatt at cs.utah.edu)
Date: Mon Aug 4 02:40:23 EDT 2014

At Mon, 4 Aug 2014 00:52:56 -0300, Henry Lenzi wrote:
> What I'm sensing is that you seem to be concerned about bugs with
> Racket Scheme's EVAL. Is that it?
> I do not understand what the problem with EVAL is. Would you please
> state clearly what the problems are?

While he didn't say so explicitly, I don't think that Neil is worried
about the implementation of `eval` within Racket. After all, `eval` is
at the heart of the implementation, and any program that you give to
Racket is going through `eval` whether or not the program calls the
`eval` function.

Speaking even more generally, I think "don't use `eval`" is intended in
much the same spirit as "don't write machine code":

Someone who says "don't write machine code" isn't worried about the
correctness of your x86 processor. They're concerned that by
programming in a relatively unstructured mode, you take on too much of
the burden of accommodating the run-time environment at the bit level,
whereas a higher-level language insulates you against may details.
(Also, a compiler can usually produce faster machine code than a human.)

Similarly, the problem with using `eval` is that you take on a burden
of managing binding at a relatively low symbolic level, and you have to
worry a lot about the dynamic context. As an extreme example, if you
write

   (eval (list '+ x x))

intending to add `x` to itself, then you have to worry about whether
`+` still means addition --- as opposed to having `+` given a different
meaning by the time the `eval` is called. In contrast,

  #lang racket
  (define (f x) (+ x x))

means that `f` always adds its argument to itself, no matter what has
happened before. (And that `f` will be way faster than using `eval`.)


There are good and robust ways to use `eval` within Racket, just like
there are good and robust ways to generate machine code. But it's
exactly the job of a programming language to encode the good ways to do
things, and that's why so many on this list are eager to see you write

 #lang prescription
 HCTZ25 30 CP 1XD

instead of having "HCTZ25 30 CP 1XD" sit in a file that is sent in some
pieces through `eval`. We've learned that's just better to encode
things as languages.


Now, for someone in your position, the difference in this specific case
is subtle, because a doctor who writes

   HCTZ25 30 CP 1XD

versus the "#lang prescription" above doesn't write `eval` either way.
It would seem that as the implementor of the language, you're the one
who properly has to deal with `eval`, and it makes sense for your
language implementation to call `eval`.

And yes... but it can be much better if you arrange for 

 #lang prescription
 HCTZ25 30 CP 1XD

to become to something like

 #lang racket/base
 (import prescription/defs)
 (hctz25 30 cp 1xd) 

where `prescripton/defs` contains

 (define hctz25 "Hydrochlorothiazide 25mg")
 ...

That is, instead of dynamically making "HCTZ25 30 CP 1XD" have the
effect of the Racket expression, actually compile it statically to the
Racket expression. Then, you're saying what you mean at a much higher
level what you intend to implement, and Racket can give you a lot more
help. For example, if a doctor accidentally types

 #lang prescription
 HCTZ25 30 CP add1

then instead of getting a weird run-time error about trying to use a
procedure as a string, then the doctor will get an error message that
`add1` is not a known identifier --- assuming that you did not intend
for `add1` to be included as part of the language. (If you intended to
include `add1`, then I think we can find any number of bindings that
you really do not intend for prescriptions authors to use.)

Again, it's possible to arrange for just the right bindings to be in
the namespace that you provide to `eval`. By always trying to write
things in terms of a language (as reflected by a module), however,
Racket can provide much better tools to help you say what you mean, and
it can provide a lot more efficiently and automatically.


There is a learning curve to doing this this way. We're working to make
that curve flatter, but he have a ways to go ourselves. But hopefully
that helps explain the general reaction to `eval` here.


Posted on the users mailing list.