ashinn / chibi-scheme

Official chibi-scheme repository
Other
1.21k stars 142 forks source link

Erroneous warning when using eval to apply a procedure by value rather than by name. #902

Closed Zambito1 closed 7 months ago

Zambito1 commented 1 year ago
(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
mnieper commented 1 year ago

"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.

Zambito1 commented 1 year ago

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)))
mnieper commented 1 year ago

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 + 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.

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.

mnieper commented 1 year ago

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))) +)
Zambito1 commented 1 year ago

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!

mnieper commented 1 year ago

This macro is a clever idea!