Racket R6RS support: syntax-case - scheme

This simple R6RS program:
#!r6rs
(import (rnrs base)
(rnrs syntax-case)
(rnrs io simple))
(define-syntax stest
(lambda (x)
(syntax-case x ()
((_ z) #'(z 0)))))
(stest display)
works with Chez, Guile and Ypsilon but not Racket. It gives me this:
test.scm:7:3: lambda: unbound identifier in the transformer
environment;
also, no #%app syntax transformer is bound
My question is, is it broken for R6RS or do I have to do something else? I'm testing with version 6.12.

The Racket implementation of R6RS is not non-compliant in this case. Indeed, if anything, it is obeying the standard more closely: your program as-written is not careful about import phases. The problem is that define-syntax evaluates its right-hand side during expand-time, as noted by section 11.2.2 Syntax definitions:
Binds <keyword> to the value of <expression>, which must evaluate, at macro-expansion time, to a transformer.
Unlike other Scheme standards, R6RS takes care to distinguish between phases, since it permits arbitrary programming at compile-time (while other Scheme standards do not). Therefore, section 7.1 Library form specifies how to import libraries at specific phases:
Each <import spec> specifies a set of bindings to be imported into the library, the levels at which they are to be available, and the local names by which they are to be known. An <import spec> must be one of the following:
<import set>
(for <import set> <import level> ...)
An <import level> is one of the following:
run
expand
(meta <level>)
where <level> represents an exact integer object.
Therefore, you need to import (rnrs base) at both the run and expand phases, and you need to import (rnrs syntax-case) at the expand phase. You can do this with the following program:
#!r6rs
(import (for (rnrs base) run expand)
(for (rnrs syntax-case) expand)
(rnrs io simple))
(define-syntax stest
(lambda (x)
(syntax-case x ()
((_ z) #'(z 0)))))
(stest display)
This program works in Racket. I have not tested if it also works on the other Scheme implementations you listed, but it ought to if they are standards-compliant.

Related

SICP Ch5 eceval compiler in Racket: set-cdr! into quoted list (not a dup)

This is not a duplicate of
set-car!, set-cdr! unbound in racket? or
of
Implement SICP evaluator using Racket
or of
How to install sicp package module in racket?,
but rather a follow-up question because the solutions proposed therein do not
work for me. First, the need: Section 5.5.5 of SICP, the compiler plus
explicit-control evaluator (code here in "ch5-eceval-compiler.scm"), are
entirely dependent on set-car! and set-cdr! into explicit quoted lists. I
would like to copy and modify this code without a complete, bottom-up rewrite in
immutable form. I'd also accept a reference to a scheme implementation that can
run the code out-of-the-box or with some minimal, straightforward adaptation,
i.e., a scheme that has set-car! and set-cdr! or some
work-around. Neither guile nor racket give me an easy time running this code.
EDIT: mit scheme will load the eceval compiler. I'm leaving the question up for those who might want to get it going in racket (I would rather, for example).
Here is a deeper explanation, including the things I explored and tried out, and
how I diagnosed the quoted list as the deepest problem. When I hand-converted
the quoted list into an mquoted nest of mlists, the code broke in much worse
ways and the rabbit hole got much deeper. I had to revert after a couple of
hours of delicate brain surgery that failed.
Here is an MVE of the kind of structure that section 5.5.5 relies on. This is small, but structurally like the real thing:
(define foo '(a b))
(set-cdr! foo '(c))
The real thing starts like this:
(define eceval
(make-machine
'(exp env val proc argl continue unev
compapp ;*for compiled to call interpreted
)
eceval-operations ;; ----------------------------------------------
'( ;; <<<<<<<<======== BIG QUOTED LIST CAUSING TROUBLE / NOT MCONSES!
;;SECTION 5.4.4, as modified in 5.5.7 ;; -------------------------------
;;*for compiled to call interpreted (from exercise 5.47)
(assign compapp (label compound-apply))
;;*next instruction supports entry from compiler (from section 5.5.7)
(branch (label external-entry))
read-eval-print-loop
(perform (op initialize-stack))
(perform
(op prompt-for-input) (const ";;; EC-Eval input:"))
...
and goes on for quite a while. The evaluator is the "machine-code" in the quoted
list, and various generated code does set-car! and set-cdr! into registers
and environment frames and other things. The code fails on load.
There seems to be no easy way to convert the evaluator into immutable form
without a full rewrite, and I'm trying to avoid that. Of course, set-car! and
set-cdr! are not available in #lang racket, and I don't think they're in
guile, either (at least guile refused to load "ch5-eceval-compiler.scm,"
throwing a mutability error, on which I did not dig deeper).
One solution proposed in
set-car!, set-cdr! unbound in racket? is
to rewrite the code using mcons, mcar, mlist, etc. according to (require
compatibility/mlist) (require rnrs/mutable-pairs-6). Those compatibility
packages have no replacement for quote, so I tried writing my own mquote. I
spent a couple of hours on such a refactoring, but the exercise was not
converging, just going deeper and deeper down the rabbit hole and ending up with
even deeper problems. It seems that to pursue even a refactoring I must
understand more semantics about "ch5-eceval-compiler.scm," and if I must, I
might as well rewrite it in immutable form.
Easier solutions proposed in
set-car!, set-cdr! unbound in racket?
are to use #lang sicp or #lang r5rs. There follows three experiments that
referenced other answers on stack overflow:
#lang r5rs
(define foo '(a b))
(set-cdr! foo '(c))
foo
Error: struct:exn:fail:contract:variable
set-cdr!: undefined;
cannot reference an identifier before its definition
in module: "/usr/share/racket/pkgs/r5rs-lib/r5rs/main.rkt"
-----------------------------------------------
which points to a place where set-cdr! is clearly defined:
...
(module main scheme/base
(require scheme/mpair
racket/undefined
(for-syntax scheme/base syntax/kerncase
"private/r5rs-trans.rkt")
(only-in mzscheme transcript-on transcript-off))
(provide (for-syntax syntax-rules ...
(rename-out [syntax-rules-only #%top]
[syntax-rules-only #%app]
[syntax-rules-only #%datum]))
(rename-out
[mcons cons]
[mcar car]
[mcdr cdr]
[set-mcar! set-car!] ;; --------------------------
[set-mcdr! set-cdr!] ;; <<<<<<<<======== LOOK HERE
[mpair? pair?] ;; --------------------------
[mmap map]
[mfor-each for-each])
= < > <= >= max min + - * /
abs gcd lcm exp log sin cos tan not eq?
call-with-current-continuation make-string
symbol->string string->symbol make-rectangular
exact->inexact inexact->exact number->string string->number
...
Here is a similar failure with #lang sicp
#lang sicp
(define foo '(a b))
(set-cdr! foo '(c))
foo
Error: struct:exn:fail:contract:variable
set-cdr!: undefined;
cannot reference an identifier before its definition
in module: "/home/rebcabin/.racket/7.2/pkgs/sicp/sicp/main.rkt"
----------------------------------------------------
pointing to code that only indirectly defines set-cdr!, but clearly in the
appropriate package:
....
#lang racket
(require racket/provide ;; --------------------------------------------
(prefix-in r5rs: r5rs) ;; <<<<<<<<======== PULL IN SET-CDR! ETC. HERE?
(rename-in racket [random racket:random])) ;; ------------------------
(provide (filtered-out (λ (name) (regexp-replace #px"^r5rs:" name ""))
(except-out (all-from-out r5rs) r5rs:#%module-begin))
(rename-out [module-begin #%module-begin]))
(define-syntax (define+provide stx)
(syntax-case stx ()
[(_ (id . args) . body) #'(begin
(provide id)
(define (id . args) . body))]
[(_ id expr) #'(begin
(provide id)
(define id expr))]))
...
I dig deeper into
Implement SICP evaluator using Racket
and find
(require (only-in (combine-in rnrs/base-6
rnrs/mutable-pairs-6)
set-car!
set-cdr!))
(define foo '(a b))
(set-cdr! foo '(c))
foo
yielding
Error: struct:exn:fail:contract
set-mcdr!: contract violation
expected: mpair?
given: '(a b)
argument position: 1st
other arguments...:
'(c)
This error implies that the problem really is the quoted list. I don't have an
easy way to make the big quoted list in eceval into an mlist or chain of
mcons. I tried it and it's very verbose and error prone, plus I think the code
that loads eceval scans and patches that list, so it uses other list operations.
I had to revert after going south in a bad way.
Perhaps I missed some way to automate the transformation, a macro, but that's a
deeper rabbit hole (my scheme macro-fu is too old).
So I'm stuck. Nothing easy or recommended works. I'd like to either (1) know a scheme implementation that will run this code (2) some way I can implement set-car! and set-cdr! in racket (3) some other kind of work around (4) or maybe I just made a stupid mistake that one of you kind people will easily fix.
I tried (by either running racket directly or running it via DrRacket)
#lang sicp
(define foo '(a b))
(set-cdr! foo '(c))
foo
and it outputs (a c).
This also works:
#lang r5rs
(define foo '(a b))
(set-cdr! foo '(c))
(display foo)
To answer your question regarding the implementation of SICP (which I am currently maintaining), you are correct that (prefix-in r5rs: r5rs) will import set-cdr!.
Update: I just installed Geiser and now experienced the same problem that you did when I C-c C-b. However, C-c C-a works as expected.
Personally, I would use racket-mode instead of Geiser.
Mit-scheme will load the eceval compiler from the code drop mentioned in the question. On Ubuntu, mit-scheme loads with sudo apt-install mit-scheme. The geiser package of emacs finds mit via run-mit. The problem is solved.

Aliases for keywords

(define defun define)
It raises error define: not allowed in an expression context in: define in Racket. How to create aliases for fundamental constructs like define, let, lambda?
define is a syntax, not a first-class object. You cannot refer to it as an object.
As Justin said, you can create a macro. But note that Lisp-style defun has different syntax to Scheme-style define, and your macro should take that into account:
(define-syntax-rule (defun name params body ...)
(define (name . params)
body ...))
Not sure about Racket specifically, but the more general problem is that in Scheme define, let and lambda are syntax and/or special forms rather than functions. So you cannot reference them in an expression context like you could if they were defined as functions.
But instead, you can define a macro defun that expands into a define expression.
With normal procedures you can alias with define:
(define first car) ; first isn't defined in R[67]RS
However define and defun isn't form compatible. This macro will make a global defun that works as in Common Lisp:
#!r6rs
(import (rnrs base))
(define-syntax defun
(syntax-rules ()
((defun name args . body)
(define (name . args) . body))))
define in Scheme has more hats than defun, mostly because of the one-namespace nature of Scheme. define works as labels, flet, defconstant and setq (but for previously bound, one need to use set! to update).

r6rs and define-record-type

HY everyone,
for class i had to import some libraries.
i got an error, after checking the libraries out, the problem basically boils down to
r6rs that gives this error : define-record-type: unbound identifier in module in: define-record-type
in this librarie :
#lang r6rs
(library
(scenario-line)
(export new say-what says-who say-it)
(import ;...
)
(define-record-type scenario-line
(new figure text)
scenario-line?
(figure says-who)
(text say-what))
(define (say-it scenario-line)
(diagonal-paste (diagonal-paste (says-who scenario-line)
(new-cloud 15 15))
(new-text-cloud (say-what scenario-line)))))
You left out your import declarations which are the most important part for figuring out unbound identifiers!
The identifier define-record-type is exported from the (rnrs records syntactic) and thus you'll need to ensure that one of your imports is:
(import (rnrs records syntactic))
From the R6RS documentation:
The syntactic layer is provided by the (rnrs records syntactic
(6))library. Some details of the specification are explained in terms
of the specification of the procedural layer below.
The record-type-defining form define-record-type is a definition and
can appear anywhere any other can appear. ...

R6RS Library body: definition after expression

Consider the following code:
#!r6rs
(library
(test)
(export)
(import (rnrs))
(define a 5)
(begin
(define b 4)
(+ 3 b))
'cont
(define c 5)
'done)
From the R6RS Report 7.1:
A <library body> is like a <body> (see section 11.3) except that a <library body>s need not include any expressions. It must have the following form:
<definition> ... <expression> ...
I thought it would emit error because definition of c is after expression 'cont, but this program is accepted cleanly.
After Then, I thought a and c could be exported. But, not c but b can be exported. (a can be exported as I thought.)
I think there are something I didn't realize about R6RS library rules. What is the point I'm missing? Thanks in advance.
p.s) I'm using Racket v5.3.3
From the R6RS 2007 specification:
A library definition must have the following form:
(library <library name>
(export <export spec> ...)
(import <import spec> ...)
<library body>)
...
The <library body> is the library body, consisting of a sequence of definitions
followed by a sequence of expressions. The definitions may be both for local
(unexported) and exported bindings, and the expressions are initialization
expressions to be evaluated for their effects.
Thus, for your example code, an error should have been raised.
Sorry, this is not the right answer. It is how the program toplevel works, not library toplevel. Leaving it here for reference.
In the program toplevel things work a bit different from normal (normal being the way you interpreted it).
The code will be rewritten by the compiler to look something like:
(define a 5)
(define b 4)
(define dummy1 (+ 3 b))
(define dummy2 'cont)
(define c 5)
'done
Notes:
begin splices in the toplevel
For any non-definition, the expression is assigned to a 'dummy' variable
The toplevel eventually ends up looking like a letrec* and the same rules apply

How to define a compile time available function in scheme?

How the scheme compiler determines, which functions will be available during macroexpansion?
I mean such low level mechanisms, like syntax-case, where you can not only generate patter substitution, but call some functions, at least in a fender part
Edit:
I mean, I need to use an ordinary function in macroexpansion process. E. g.:
(define (twice a)
(declare 'compile-time)
(* 2 a))
(let-syntax ((mac (lambda (x)
(syntax-case x ()
((_ n) (syntax (display (unsyntax (twice n)))))))))
(mac 4))
Where n is known to be a number, and evaluation of (twice n) occurs during expansion.
Every Scheme compiler determines the functions referenced by a macro expansion. In your case, the compilation of 'let-syntax' will result in the compiler determining that 'twice' is free (syntactically out-of-scope within 'let-syntax'). When the macro is applied, the free reference to the 'twice' function will have been resolved.
Different Scheme compilers perform the free value resolution at possibly different times. You can witness this by defining 'twice' as:
(define twice
(begin (display 'bound')
(lambda (x) (* 2 x))))
[In your case, with let-syntax it will be hard to notice. I'd suggest define-syntax and then a later use of '(mac 4'). With that, some compilers (guile) will print 'bound' when the define-syntax is compiled; others (ikarus) will print 'bound' when '(mac 4)' is expanded.]
It depends what macro system you are using. Some of these systems allow you to call regular scheme functions during expansion. For example, Explicit Renaming Macros let you do this:
(define-syntax swap!
(er-macro-transformer
(lambda (form rename compare?)
...
`(let ((tmp ,x))
(set! ,x ,y)
(set! ,y tmp)))))
That said, the macro systems available to you will depend upon what Scheme you are using.

Resources