chibi-scheme
chibi-scheme copied to clipboard
Erroneous warning when using eval to apply a procedure by value rather than by name.
(import (scheme base) (scheme eval))
(eval (list + 1 2) (environment))
The above eval
should (and does) return 3. It is essentially the same as (apply + (list 1 2))
, in the sense that +
comes from the current lexical environment, rather than the environment
parameter. eval
warns about an "invalid operator in application", but I don't think it should.
The same error occurs whether the application is on an opcode
or a procedure
value.
> (eval (list + 1 2) (environment))
WARNING: invalid operator in application: (#<opcode "+"> 1 2)
3
> (eval (list display "test\n") (environment))
WARNING: invalid operator in application: (#<procedure display> "test\n")
test
"It is an error" if the first argument to eval is not a Scheme datum value. (eval (list + 1 2) (environment ...))
is not standard Scheme; (eval (list '+ 1 2) (environment ...))
is.
+
does not "come from the current lexical environment" in the sense that +
comes from the environment returned by (environment ...)
. In the latter case, +
has to be looked up first.
Hi Marc,
"It is an error" if the first argument to eval is not a Scheme datum value.
Where is this stated? This is what I can find on eval
from R7RS:
(eval expr-or-def environment-specifier) eval library procedure If expr-or-def is an expression, it is evaluated in the speci- fied environment and its values are returned. If it is a defi- nition, the specified identifier(s) are defined in the specified environment, provided the environment is not immutable. Implementations may extend eval to allow other objects.
Is a value (such as the value referred to by +
or display
in the original issue) not considered an "expression"? Of course it is not a symbolic expression where the symbols need to be looked up in the environment. But to me it seems like any atomic value should evaluate to itself. Even if this does fall under "Implementations may extend eval to allow other objects", it seems like the following should be considered valid without warning by Chibi:
(eval (list + 1 2) (environment))
To match the following existing behavior which evaluates without warning in Chibi:
(eval 1 (environment))
(let ((x 1)) (eval (list '+ x 2) (environment '(scheme base))))
((eval + (environment)) 1 2)
Edit: Another interesting example without warning:
;; even? used as a procedure from the current lexical environment, but not in an application position
(eval (list 'filter even? '(list 1 2 3 4)) (environment '(only (scheme base) list) '(srfi 1)))
Hi Marc,
"It is an error" if the first argument to eval is not a Scheme datum value.
Where is this stated? This is what I can find on
eval
from R7RS:(eval expr-or-def environment-specifier) eval library procedure If expr-or-def is an expression, it is evaluated in the speci- fied environment and its values are returned. If it is a defi- nition, the specified identifier(s) are defined in the specified environment, provided the environment is not immutable. Implementations may extend eval to allow other objects.
R6RS, which you may want to look up, is more precise. But what is meant here is that expr-or-def
represents (as a Scheme value) an expression (or definition) as described in chapters 4 and 5 of R7RS.
Expressions (and definitions) are represented by Scheme datum values because this is what the Scheme (code) reader produces.
Is a value (such as the value referred to by
+
ordisplay
in the original issue) not considered an "expression"? Of course it is not a symbolic expression where the symbols need to be looked up in the environment. But to me it seems like any atomic value should evaluate to itself.
It is not an expression as defined by R7RS. Please also take a look at 4.1.2 about literal expressions.
Even if this does fall under "Implementations may extend eval to allow other objects", it seems like the following should be considered valid without warning by Chibi:
Yes, it does fall under "implementation-specific extensions". A warning is not necessarily bad because it can help write portable programs.
(eval (list + 1 2) (environment))
To match the following existing behavior which evaluates without warning in Chibi:
(eval 1 (environment)) (let ((x 1)) (eval (list '+ x 2) (environment '(scheme base)))) ((eval + (environment)) 1 2)
The first two expressions are standard R7RS. Chibi is inconsequential by not issuing a warning for the last expression. This is due to how literal expressions are implemented.
The same issue shows up in the "another interesting" example.
PS If you need to "inject" values into an expression to be evaluated in a portable way, you can use the following idiom:
((eval '(lambda (plus) (plus 1 2)) (environment '(scheme base))) +)
I think I understand better now :) the duality between literal expressions and their symbolic values made it seem inconsistent to me.
I also just made this macro based on that pattern:
(define-syntax eval-with
(syntax-rules ()
((_ (injections ...) expr env)
((eval `(lambda (injections ...) ,expr) env) injections ...))))
(eval-with (+) '(+ 1 2) (environment '(only (scheme base) lambda))) ; => 3, without warning
Thank you for helping me understand!
This macro is a clever idea!