Eval in a function

hello,

how can i make this eval working as in toplevel:

#lang racket
(define (foo)
  (define x 7)
  (eval (quote x)))

Welcome to DrRacket, version 8.7 [cs].
Language: racket, with debugging; memory limit: 8192 MB.

(foo)
. . x: undefined;
cannot reference an identifier before its definition
(define x 7)
(eval (quote x))
7

Like this:

(define ns (make-base-namespace))
(define (foo)
  (displayln (eval '(define x 7) ns))
  (displayln (eval 'x            ns)))
(foo)

Note that you need to evaluate (define x 7).
There is by design no way to access local variables via eval.

too bad... i'm writing an infix evaluator with precedence and i need to use the already defined variable at any level,for example in a function or a macro.

Don't quote anything; don't use eval.

Use syntax objects (syntax->list and #‵(...)) and keep the identifiers so the scopes are intact. To see if two identifiers are "the same", like comparing +, *, etc., use free-identifier=?.

yes i have other versions with limited features that do not use quote and eval.

"syntax object" : i do not want to dive into that , i'm so ignorant about that i would have for weeks, not portable too

Just pattern-based macros that recognize outermost operators should work too like this example. Afterwards, replace printf-reverse by calls to some infix-to-postfix conversion algorithm and use the op struct to identify operators.

#lang racket/base

(struct op (sym) #:transparent)

(define-syntax mark-$-ops
  (syntax-rules (+ - * /)
    [(_ () (e-acc ...))
     (list e-acc ...)]
    [(_ (+ e ...) expr-acc) (mark-$-ops (e ...) ((op '+) . expr-acc))]
    [(_ (- e ...) expr-acc) (mark-$-ops (e ...) ((op '-) . expr-acc))]
    [(_ (* e ...) expr-acc) (mark-$-ops (e ...) ((op '*) . expr-acc))]
    [(_ (/ e ...) expr-acc) (mark-$-ops (e ...) ((op '/) . expr-acc))]
    [(_ (expr0 e ...) expr-acc) (mark-$-ops (e ...) (expr0 . expr-acc))]))

(define-syntax $
  (syntax-rules ()
    [(_ e ...)
     (printf "TODO: parse and convert to postfix\n~a\n"
             (reverse (mark-$-ops (e ...) ())))]))

(define y 456)

(define (compute x)
  (let ([z 7])
    ($ x + 3 * y + z / 2)))

(compute 9)

Here is the result of macro expansion displayed in the macro stepper:

yes pattern based is a way i think ,i suppose it can solve the overloaded operator problem ,but notice that when dealing operator one by one in the pattern , a user can have defined a new operator not in the pattern cases ,but perheaps i did not well understood your code if expr0 deal with specific user defined operator...

note about overload operator problem: it is the fact that when you want to compare the operator to find precedence rules you must compare quoted operators or symbols ,if you use procedure comparaison the <procedure +> could be different from <new procedure + overloaded> a way i find to solve the operator overloaded problem is quote all operators and evaluate them later (works in Guile) or rebuild a precedence list of operator each time i call the infix with precedence evaluation. Then in the precedence list :

(define infix-operators
  
  (list
   
   (list expt **)
   (list * / %)
   (list + -)
   
   (list << >>)

   (list & ∣ )

   (list < > = <> ≠ <= >=)
   
   
   ;;(list 'dummy) ;; can keep the good order in case of non left-right assocciative operators.(odd? reverse them) 
   
   
   )
  )

the operators are up to date with the last definitions known. (note that in the previous list operators are NOT quoted)

and this solution is only working if i have my scheme-infix.rkt module included , it is not working if required in a module because the data are then encapsulated in the module and again i have to test that in real code,not only in simple examples sometimes only made at REPL, anyway a good solution seems to make all the overload before the scheme-infix.rkt is read,so the code included (not required) know all the procedures bind to operators symbols.

The code as written only recognizes the bindings of +, -, * and / at the outermost level. Of course, if you don't want to recognize operators by bindings, it is possible to compare operators by symbols.

However, The name of the set of operators that $ recognizes could be configurable at expansion time or in user-provided code, but must be fixed before the code starts running and especially must be determined before any uses of the $ macro. At runtime, of course, you can provide a different interpretation of the names. A possible way is to have the configuration written in a separate module.

Unless you are using the top level i.e. the REPL, Racket won't let you redefine bindings. Note that the expansion time and the run time are separated, so it is important to add new operators to both expansion and run time. The macro add-new-operator implements one possible idea:

In a file ops.rkt:

#lang racket/base

(provide op-map)

(define op-map
  (make-hash
   (list (cons 'expt expt)
         (cons '* *)
         (cons '/ /)
         (cons '+ +)
         (cons '- -)
         (cons '= =))))

In the macro definition file, dollar.rkt:

#lang racket/base

(provide $ add-new-operator)

(struct op (sym) #:transparent)

(require (for-syntax racket/base "ops.rkt"))
(define-syntax (mark-$-ops stx)
  (syntax-case stx ()
    [(_ () (e-acc ...))
     #'(list e-acc ...)]
    [(_ (rator e ...) expr-acc)
     (and (identifier? #'rator) (hash-has-key? op-map (syntax-e #'rator)))
     #'(mark-$-ops (e ...) ((op 'rator) . expr-acc))]
    [(_ (expr0 e ...) expr-acc)
     #'(mark-$-ops (e ...) (expr0 . expr-acc))]))

(define-syntax $
  (syntax-rules ()
    [(_ e ...)
     (interp (reverse (mark-$-ops (e ...) ())))]))

(require "ops.rkt")
(define (interp expr)
  (printf "expr = ~s\n" expr)
  (for ([e (in-list expr)])
    (when (op? e)
      (printf "op ~s maps to: ~s\n" e (hash-ref op-map (op-sym e))))))

(define-syntax (add-new-operator stx)
  (syntax-case stx ()
    [(_ new-op)
     (let ()
       (hash-set! op-map (syntax-e #'new-op) #f)
       #'(hash-set! op-map 'new-op new-op))]))

Here is an example use that interprets = as eqv? and adds quotient to the list of operators:

#lang racket/base

(require "dollar.rkt" "ops.rkt")
(hash-set! op-map '= eqv?)

(define y 456)

(add-new-operator quotient)
(define (compute x)
  (let ([z 7])
    ($ x + 3 * y = quotient z 2)))

(compute 9)

i agree with you , my solution works only at REPL in Racket, but is ok in Guile at REPL and toplevel (not in procedure of course)

i'm searching a solution, first understand what is the difference between Guile and Racket modules? ...

i isolated the problem (because as i use a SRFI-105 REPL parser and modules) if you could have a look at.

Again this works in Guile , not Racket:

file test-racket.rkt:

#lang racket

(include "overload.scm")

(display "before plus") (newline)
+

(display "before add-vect-vect") (newline)
(define (add-vect-vect v1 v2) (map + v1 v2))
(display "before overload") (newline)


(overload + add-vect-vect (list? list?) 'operator)

my overload.scm file that works well in Guile REPL and top-level files but works only in Racket REPL:

;; overload
;; use with Scheme+:



;; scheme@(guile-user)> (use-modules (Scheme+))
;; scheme@(guile-user)> (define (add-vect-vect v1 v2) (map + v1 v2))
;; scheme@(guile-user)> (overload + add-vect-vect (list? list?) 'operator)
;; create-overloaded-operator : pred-list = (#<procedure list? (_)> #<procedure list? (_)>)
;; funct: #<procedure add-vect-vect (v1 v2)>
;; orig-funct: #<procedure + (#:optional _ _ . _)>
;; old-funct: #<procedure + (#:optional _ _ . _)>
;; new-funct: #<procedure new-funct args>
;; scheme@(guile-user)> (+ '(1 2 3) '(4 5 6) '(7 8 9))
;; new-funct: new-funct = #<procedure new-funct args>
;; new-funct : pred-list = (#<procedure list? (_)> #<procedure list? (_)>)
;; new-funct : args = ((1 2 3) (4 5 6) (7 8 9))
;; new-funct : nb-args = 3
;; (12 15 18)

(define-syntax overload

  (syntax-rules ()

    ;; arguments are symbol of function to be overloaded, procedure that do the overloading, list of predicate to check the arguments
    ((_ funct-symb proc (pred-arg1 ...)) (overload-procedure funct-symb proc (pred-arg1 ...)))
    ((_ funct-symb proc (pred-arg1 ...) quote-operator) (overload-operator funct-symb proc (pred-arg1 ...)))))



;; (define (mult-num-vect k v) (map (λ (x) (* k x)) v))
  
;; (overload * mult-num-vect (number? list?) 'operator) 

;; (* 3 (+ '(1 2 3) '(4 5 6)))

;; (15 21 27)

;; (+ (* 3 '(1 2 3)) '(4 5 6))
;; (7 11 15)

;; {3 * '(1 2 3) + '(4 5 6)}
;; (7 11 15)

;; {3 * '(1 2 3) + '(4 5 6) + '(7 8 9)}
;; (14 19 24)


;; scheme@(guile-user)> {3 * '(1 2 3)}
;; $3 = (3 6 9)

;; scheme@(guile-user)> (define (add-vect v) v)
;; scheme@(guile-user)> (overload + add-vect (list?) 'operator)
;; scheme@(guile-user)> (+ '(1 2 3))
;; $7 = (1 2 3)

;; scheme@(guile-user)> (define (add-pair p1 p2) (cons (+ (car p1) (car p2)) (+ (cdr p1) (cdr p2))))
;; scheme@(guile-user)> (overload + add-pair pair? pair?)
;; overload
;; scheme@(guile-user)> (+ (cons 1 2) (cons 3 4))
(define-syntax overload-procedure
  
  (syntax-rules ()

    ((_ orig-funct funct (pred-arg1 ...)) 
     (define orig-funct (create-overloaded-procedure orig-funct funct (list pred-arg1 ...))))))

      ;;'(define orig-funct (create-overloaded-procedure-macro orig-funct funct (list pred-arg1 ...))))))


(define-syntax overload-operator
  
  (syntax-rules ()

    ((_ orig-funct funct (pred-arg1 ...))
     (define orig-funct (create-overloaded-operator orig-funct funct (list pred-arg1 ...))))))
 
     ;;(define orig-funct (create-overloaded-operator-macro orig-funct funct (list pred-arg1 ...))))))


(define (check-arguments pred-list args)
  (if (= (length pred-list) (length args))
      (let ((pred-arg-list (map cons pred-list args)))
	(andmap (λ (p) ((car p) (cdr p)))
	;; replace andmap with every
	;;(every (λ (p) ((car p) (cdr p)))
		pred-arg-list))
      #f))




;; (define (add-list-list L1 L2) (map + L1 L2))
;; (define + (overload-proc + add-list-list (list list? list?)))
;; (+ '(1 2 3) '(4 5 6))
;; (define (add-pair p1 p2) (cons (+ (car p1) (car p2)) (+ (cdr p1) (cdr p2))))
;; (define + (overload-proc + add-pair (list pair? pair?)))
;; (+ (cons 1 2) (cons 3 4))


;; when a function that overload an operator has more than 2 args (f a1 a2 a3 ...) and only (f a1 a2) is defined
;; we do: (f a1 (f a2 a3 ...)) for operators like: + - * / ^ and other if any... we detect those operators to separate distinct case from simple functions.
;; note: this could not be done recursively in the general case, we need an hash table
(define (create-overloaded-procedure orig-funct funct pred-list)

  (display "create-overloaded-procedure")
  (display " : pred-list = ") (display pred-list) (newline)
  (define old-funct orig-funct)
  (define new-funct (lambda args ;; args is the list of arguments
		      (display "new-funct: ") (display new-funct) (newline)
		      (display "new-funct : pred-list = ") (display pred-list) (newline)
		      (display "new-funct : args = ") (display args) (newline)
		      (if (check-arguments pred-list args)
			  (begin
			    (display "new funct :calling:") (display funct) (newline)
			    (apply funct args))
			  (begin
			    (display "new funct :calling:") (display old-funct) (newline)
			    (apply old-funct args)))))
				    
  (display "funct: ") (display funct) (newline)
  (display "orig-funct: ") (display orig-funct) (newline)
  (display "old-funct: ") (display old-funct) (newline)
  (display "new-funct: ") (display new-funct) (newline)

  new-funct)


;; scheme@(guile-user)> (use-modules (overload))
;; ;;; note: source file /usr/local/share/guile/site/3.0/overload.scm
;; ;;;       newer than compiled /Users/mattei/.cache/guile/ccache/3.0-LE-8-4.6/usr/local/share/guile/site/3.0/overload.scm.go
;; ;;; note: auto-compilation is enabled, set GUILE_AUTO_COMPILE=0
;; ;;;       or pass the --no-auto-compile argument to disable.
;; ;;; compiling /usr/local/share/guile/site/3.0/overload.scm
;; ;;; compiled /Users/mattei/.cache/guile/ccache/3.0-LE-8-4.6/usr/local/share/guile/site/3.0/overload.scm.go
;; scheme@(guile-user)> (define (add-vect-vect v1 v2) (map + v1 v2))
;; scheme@(guile-user)> (overload + add-vect-vect (list? list?) 'operator)
;; create-overloaded-operator : pred-list = (#<procedure list? (_)> #<procedure list? (_)>)
;; funct: #<procedure add-vect-vect (v1 v2)>
;; orig-funct: #<procedure + (#:optional _ _ . _)>
;; old-funct: #<procedure + (#:optional _ _ . _)>
;; new-funct: #<procedure new-funct args>
;; scheme@(guile-user)> (+ '(1 2 3) '(4 5 6))
;; new-funct: #<procedure new-funct args>
;; new-funct : pred-list = (#<procedure list? (_)> #<procedure list? (_)>)
;; new-funct : args = ((1 2 3) (4 5 6))
;; new funct :calling:#<procedure add-vect-vect (v1 v2)>
;; $1 = (5 7 9)
;; scheme@(guile-user)> (+ 2 3)
;; new-funct: #<procedure new-funct args>
;; new-funct : pred-list = (#<procedure list? (_)> #<procedure list? (_)>)
;; new-funct : args = (2 3)
;; new funct :calling:#<procedure + (#:optional _ _ . _)>
;; $2 = 5
;; scheme@(guile-user)> (+ '(1 2 3) '(4 5 6) '(7 8 9))
;; new-funct: #<procedure new-funct args>
;; new-funct : pred-list = (#<procedure list? (_)> #<procedure list? (_)>)
;; new-funct : args = ((1 2 3) (4 5 6) (7 8 9))
;; $3 = (12 15 18)

;; scheme@(guile-user)> {'(1 2 3) + '(4 5 6) + '(7 8 9)}
;; new-funct: new-funct = #<procedure new-funct args>
;; new-funct : pred-list = (#<procedure list? (_)> #<procedure list? (_)>)
;; new-funct : args = ((1 2 3) (4 5 6) (7 8 9))
;; new-funct : nb-args = 3
;; (12 15 18)

(define (create-overloaded-operator orig-funct funct pred-list) ;; works for associative operators

  (display "create-overloaded-operator")
  (display " : pred-list = ") (display pred-list) (newline)
  (define old-funct orig-funct)
  (define new-funct (lambda args ;; args is the list of arguments
		      (display "new-funct: new-funct = ") (display new-funct) (newline)
		      (display "new-funct : pred-list = ") (display pred-list) (newline)
		      (display "new-funct : args = ") (display args) (newline)
		      (define nb-args (length args))
		      (display "new-funct : nb-args = ") (display nb-args) (newline)
		      (cond ((check-arguments pred-list args) (begin
								(display "new funct :calling:") (display funct) (newline)
								(apply funct args)))
			    ((> nb-args 2) (new-funct (car args) (apply new-funct (cdr args)))) ;; op(a,b,...) = op(a,op(b,...))
			    (else
			     (begin
			       (display "new funct :calling: ") (display old-funct) (newline)
			       (apply old-funct args))))))
				    
  (display "funct: ") (display funct) (newline)
  (display "orig-funct: ") (display orig-funct) (newline)
  (display "old-funct: ") (display old-funct) (newline)
  (display "new-funct: ") (display new-funct) (newline)

  new-funct)

(define-syntax create-overloaded-operator-macro

  (syntax-rules ()

     ((_ orig-funct funct pred-list) ;; works for associative operators

      (begin ;; will cause Error with define not allowed in this expression context !!!
	(display "create-overloaded-operator")
	(display " : pred-list = ") (display pred-list) (newline)
	(define old-funct orig-funct)
	(define new-funct (lambda args ;; args is the list of arguments
			    (display "new-funct: new-funct = ") (display new-funct) (newline)
			    (display "new-funct : pred-list = ") (display pred-list) (newline)
			    (display "new-funct : args = ") (display args) (newline)
			    (define nb-args (length args))
			    (display "new-funct : nb-args = ") (display nb-args) (newline)
			    (cond ((check-arguments pred-list args) (begin
								      (display "new funct :calling:") (display funct) (newline)
								      (apply funct args)))
				  ((> nb-args 2) (new-funct (car args) (apply new-funct (cdr args)))) ;; op(a,b,...) = op(a,op(b,...))
				  (else
				   (begin
				     (display "new funct :calling: ") (display old-funct) (newline)
				     (apply old-funct args))))))
	
	(display "funct: ") (display funct) (newline)
	(display "orig-funct: ") (display orig-funct) (newline)
	(display "old-funct: ") (display old-funct) (newline)
	(display "new-funct: ") (display new-funct) (newline)
	
	new-funct))))



;; (overload-function (+ (L1 list?) (L2 list?)) (map + L1 L2)) ;; bad example ,it is not a function but an operator!
(define-syntax overload-function
  
  (syntax-rules ()

    ((_ (orig-funct (arg1 pred-arg1) ...) expr ...) (overload orig-funct (lambda (arg1 ...) expr ...) (pred-arg1 ...)))))



the error is caused by the overload, after that + is unknow!!!??? i do not understand (again works with guile and the same macros)

Welcome to DrRacket, version 8.7 [cs].
Language: racket, with debugging; memory limit: 8192 MB.
before plus
. . +: undefined;
 cannot reference an identifier before its definition

note: i admit i have warning in guile i never have: " warning: non-idempotent binding for +'. When first loaded, value for +` comes from imported binding, but later module-local definition overrides it; any module reload would capture module-local binding rather than import." I suppose Racket is more strict.

At run time, the expressions are evaluated from top to bottom and from left to right. However, the bindings are determined at expansion time and have nothing to do with run time.

For modules, the bindings are mutually recursive. This is what the code looks like after expansion (check the macro stepper):

   #lang racket
   (display "before plus")
   (newline)
   +
   (display "before add-vect-vect")
   (newline)
   (define (add-vect-vect v1 v2) (map + v1 v2))
   (display "before overload")
   (newline)
   (define +
     (create-overloaded-operator
      +
      add-vect-vect
      (list list? list?)))))

Because module-level bindings are mutually recursive, the + in line 4 refers to the + define at the end of the code. Therefore, at run time line 4 of the code tries to use the value of + that has not been evaluated yet, leading to the error you see. This is the same class of runtime errors as this piece of code:

#lang racket
(+ x y) ;; <- x and y has not been evaluated yet
(define x 5)
(define y 8)

my problem is this code works in Guile , in a file:

(define (foo p) p)

(define + (foo +))

but not in a Racket file:

+: undefined;
cannot reference an identifier before its definition

any idea to force the Guile behavior?

Sol 1: Use #lang racket/load to use the REPL language.

Sol 2: Use

(require (prefix-in r: racket/base))
(define (foo p) p)
(define + (foo r:+))

What you are asking is precisely the behavior of the REPL, contradicting to the semantics of modules.

yes i had noticed about the top to bottom evaluation and moved the code to the good place (seems i had not this problem in Guile?) and try the
#lang racket/load it works but i cannot use it with my
#lang reader "SRFI-105.rkt"
if i put #lang racket/load replacing #lang racket it does not work with the srfi 105 code reader and parser.

i got this error:

Welcome to DrRacket, version 8.7 [cs].
Language: reader "SRFI-105.rkt", with debugging; memory limit: 8192 MB.
. . ../../../../../../../../Applications/Racket v8.7/collects/compiler/private/cm-minimal.rkt:741:23: standard-module-name-resolver: collection not found
for module path: racket/lang/lang/reader
collection: "racket/lang/lang"
in collection directories:
/Users/mattei/Library/Racket/8.7/collects
/Applications/Racket v8.7/collects/
... [170 additional linked and package directories]
no package suggestions are available .

Interactions disabled.

my SRFI-105.rkt begin like this:



#lang racket/lang  ;; use to be racket
(compile-enforce-module-constants #f)
(require syntax/strip-context)

(provide (rename-out [literal-read read]
                     [literal-read-syntax read-syntax]))


(define (literal-read in)
  (syntax->datum
   (literal-read-syntax #f in)))
 
(define (literal-read-syntax src in)
  
  (define lst-code (process-input-code-tail-rec in))

  (strip-context `(module anything racket ,@lst-code)))
 

;; read all the expression of program
 
...

Try something like

;; custom reader
#lang racket

(require syntax/strip-context)

(provide (rename-out [literal-read read]
                     [literal-read-syntax read-syntax]))


(define (literal-read in)
  (syntax->datum
   (literal-read-syntax #f in)))
 
(define (literal-read-syntax src in)
  
  (define lst-code (process-input-code-tail-rec in))

  (strip-context `(module anything racket/load (compile-enforce-module-constants #f) ,@lst-code)))

yes it worked but now the 'require' is forbidden and with SRFI-105 this is a problem because the reader infix does not include and parse recursively the files, for this reason i used require with a #lang reader "SRFI-105.rkt", this is hard to solve.

What does require forbidden or parse recursively means? The code I posted roughly works like this:

Some reader file, like load-reader.rkt -- for example, modifying the SRFI-105 reader, and the client code of this reader can use require

;; custom reader
#lang racket

(require syntax/strip-context)

(provide (rename-out [literal-read read]
                     [literal-read-syntax read-syntax]))


(define (literal-read in)
  (syntax->datum
   (literal-read-syntax #f in)))

(define (literal-read-syntax src in)
  (define lst-code (read-all-and-print in))
  (strip-context `(module anything racket/load (compile-enforce-module-constants #f) ,@lst-code)))

(define (read-all-and-print in)
  (define expr (read in))
  (if (eof-object? expr)
      '()
      (list* `(printf "Running ~s\n" ',expr)
             expr
             (read-all-and-print in))))

Some client code:

#lang reader "load-reader.rkt"

(displayln (+ 3 4))

(require racket/unsafe/ops)
(displayln unsafe-fx+)

Have you experimented with (compile-enforce-module-constants #f) ?

yes ,tried at many places, no effect.

I think it would be nice to take a step back and think about what you really want to do. Who will be the users of this library? How will the library be used? Do you intend that regular Racket programs are able to require your library, or is it in its own ecosystem entirely?

From a quick glance, it looks like you are circumventing the module system and hygienic macro system, and using reflection liberally. Those are things that regular Racket programs would not do, unless there is a really good justification. I wouldn’t be surprised if right now Racket programs can’t interop well with the library.

2 Likes