3

I follow the instructions at 3.3.3 of SICP to create a table.

Here is code_0.scm:

;code_0.scm
#lang scheme
(require rnrs/base-6)
(require rnrs/mutable-pairs-6)

(define nil '())

(define (make-table)
  (list '*table*))

(define (assoc key records)
  (cond ((null? records)
         false)
        ((equal? key (caar records))
         (car records))
        (else
         (assoc key (cdr records)))))

(define (insert! key value table)
  (let ((record (assoc key (cdr table))))
    (if record
        (set-cdr! record value)
        (set-cdr! table
                  (cons (cons key value)
                        (cdr table)))))
  'OK)

(define (lookup key table)
  (let ((record (assoc key (cdr table))))
    (if record
        (cdr record)
        false)))


(define table (make-table))

(insert! 0 0 table)
(insert! 1 1 table)
(insert! 2 2 table)

code_0.scm is working well, but not after becoming the external reference file of code_1.scm:

;I delete the #lang scheme at code_0.scm at this moment

;code_1.scm
#lang scheme/load
(load "code_0.scm")

(define table-0 (make-table))
(insert! 0 0 table-0)
(insert! 1 1 table-0)
(insert! 2 2 table-0)

error shows in DrRacket:

assoc: not a proper list: {{0 . 0}}

According to a previous question I pulled up, this happened because the "assoc" function had already defined in the Scheme library(or the DrRacket library?) that the compiler choose to link the standard/system one prior than mine.

So, is it possible to change the order DrRacket/Scheme search/reference the library?

If its a yes, how?

If its a no, is this a defect about the compiler or the language?

Is there another way to avoid this besides implementing it right in the "main" file if I have to build a name-duplicate function?

Community
  • 1
  • 1
Rahn
  • 4,787
  • 4
  • 31
  • 57

3 Answers3

10

This is not a direct answer for your problem, but a description of the mess you've gotten yourself into which eventually got to that problem, and will lead to other problems too.

I'll try to first explain how Racket evaluates a module very briefly, since it'll be relevant to understanding the mess. It looks like you're trying to use "just scheme" so you're probably not too interested in this, but you should still read it to understand the problems you're in. (Even if you solved that particular one.)

In the usual case of .rkt files each file is evaluated in its own namespace, which is populated by bindings from (1) the #lang that you specify, and (2) various libraries (= other modules) that you require. So when you have

#lang foo
(require bar)

you start from a fresh namespace, grab all of the bindings from foo into this namespace, and then you add the bindings from bar. In case of clashes, the require bindings will shadow ones from the language, so if both of them provided some function f, the name that your code will use will be the one from bar. If you require more than one library:

#lang foo
(require bar baz)

and both bar and baz provide f, then you'll get an error if these are different fs. This can happen if, for example, bar provides the builtin cons and baz provides a cons that creates mutable pairs (ie, provides the builtin mcons as cons). You will not get an error if both of them provide the same f though.

Again, note that this is different from the "initial bindings" which is what you get from the #lang -- a latter require will just shadow them if the same name is used. The reason for the #lang bindings treated differently is that they provide some basic bindings that the code uses at a fundamental level. For example, the language bindings will get you the require that you use later on. Another example is a #%module-begin macro that wraps the whole module body -- a tool that can be used to transform all expressions into something else, for example, this is how toplevel expressions in the racket language get their value printed.

Roughly speaking, libraries (= modules) in racket are split into language modules and library modules. This is not something that is formal since both are implemented in the same way: modules that provide stuff. The difference is in the kind of stuff that they provide, where language modules will usually provide lots of bindings, include basic ones like require and #%module-begin, and things that you expect from a lispish language like define, if, cond, +, cons, etc.

In the usual case, therefore, you don't run into problems of name clashes too much since libraries try to avoid common names. But if you try to require a language module as if its a library, you very quickly run into such problems since language modules tend to provide lots of names, including those very common names like the ones I listed above.

Now you can see how this is a problem in your code0.scm:

#lang scheme
(require rnrs/base-6)
(require rnrs/mutable-pairs-6)

what this does is use the scheme bindings first. This scheme language is not standard scheme -- it's the precursor to the racket language, dating to before the name change when Racket was known as PLT Scheme, and therefore scheme was intended to be "the scheme dialect that PLT Scheme uses by default". Among other things, it has immutable pairs, same as the ones you get in #lang racket files.

But later on you pile up most of the rnrs bindings -- and those use mutable pairs, which are, as you've discovered, a different type. You're requiring a module that is usually used as a language, so most of the bindings you use will work fine, but sooner or later you'll run into a binding from scheme that is not shadowed by one from rnrs, and if it's something that has to do with pairs, you'll get problems.

So the conclusion here is to avoid mixing up two languages like this: either stick to #lang scheme or to #lang r6rs. In the former case, you can just as well switch to #lang racket, and use the usual racket libraries, and in the latter case you should be careful and try to avoid importing racket libraries that expect immutable pairs (and more). Alternatively, if your goal is to do some SICP, then use the SICP language that Neil Van Dyke wrote.

But you have more problems, still. In your code_1.scm you're using scheme/load as the language, and that's something that might make it possible to do what you're trying to do -- but it brings with it a whole pile of other problems. I you look up the documentation for scheme/load (which will send you to the docs for racket/load, the more modern name), you'll see some stuff about eval and load. This is because at some point people wanted to be able to write a single file that has several modules in it, but that was not possible. (Now it is, and you get submodules -- but racket/load is still kept around.) scheme/load was implemented to address this: using it is as if you're entering expressions in a single REPL, so you can define a bunch of modules and use them. But it's an odd language because of this, and if you don't want that particular feature, you should avoid it.

The load in the name is something that should have actually been something that discourages people from using it... The thing is that load is an old and primitive tool for structuring code, which was the only available thing in the standard language up to R5RS. The thing that it does is (again, this is a rough description) read expressions from a file and evaluate them. The problem is that you get a single namespace for everything, and worse, each definition can actually be a mutation of a previous definition if one existed. This means that even simple definitions like

(define (add1 x) (+ 1 x))

are not safe, since a later load of a file can redefine + in a way that breaks this definition. In short, this is very much a mess in a world where many libraries provide you with the same names but with different semantics. IOW, it's usually a mess in Racket. Racket (and later, R6RS) uses modules in a way that sorts the whole thing out in a much better way -- there's no single namespace and mutation, just names that are provided from closed modules. Actualy, the mutation thing is still there, but it is more restricted in the damage you can get (it's used only in the REPL); and load is there too, together with eval, and they are generally avoided as tools to organize source files.

Using load also explains why you had to remove the #lang line when you've used it -- when you load a file with a module definition, you get just that definition -- the module's. To actually use the things that the module provides, you'd also need to add a (require 'code_0). Yet another thing is that dropping the #lang line is usually a disaster since you're left without the necessary bindings for any reasonable piece of code -- but in your case, you required a whole language later on, which is how things continued to work, only with subtle differences.

So the second highlevel conclusion is to avoid load -- it's a bad tool. Also, avoid the scheme/load or racket/load languages unless you really know what they're doing and need that functionality.

Eli Barzilay
  • 29,301
  • 3
  • 67
  • 110
2

The error you see assoc: not a proper list: {{0 . 0}} needs an explanation.

In the Racket language you can create immutable pairs with cons and mutable pairs with mcons. Normally in the Scheme language cons also creates immutable pairs - but since you have the lines

(require rnrs/base-6)
(require rnrs/mutable-pairs-6)

all the standard list functions are replaced with ones that create mutable ones.

Note that mutable and immutable pairs are two totally separate datatypes - even though they are both called pairs.

Somewhere in the system there are defined primitives, say, prim-mcons and prim-icons that create mutable and immutable pairs. The #lang scheme binds cons to prim-mcons and #lang racket binds cons to prim-icons.

This has an effect on all standard functions working on lists. The assoc from rnrs/mutable-pairs-6 expects a list made of mutable pairs, and the Racket assoc expects a list made of immutable pairs. Hence the error.

How can one spot this? In the Racket language where you can use both mcons and cons the standard list operations creates lists with immutable pairs and the printer prints them using (...). Mutable pairs are printed with curly braces {...}.

The error

assoc: not a proper list: {{0 . 0}}

shows the association lists with curly braces. That means that the association list was created in the Scheme using mutable pairs.

To fix your problem, you need to use the correct assoc. You can do that by adding the lines

(require rnrs/base-6)
(require rnrs/mutable-pairs-6)

to your second file as well.

Note: the #lang scheme language isn't one of the RnRS Schemes, but the dialect that Racket used before the project changed name.

Note 2: Why did Racket remove mutability from pairs in the first place? Well, in well-written Scheme code it is rare to see set-car! and set-cdr! in use. Having the guarantee that a pair is never mutated allows various optimizations that allow most programs to run faster (for one length can be implemented to use constant time). The choice was therefore made to make immutable pairs the standard and keep mutable pairs around for the those programs that absolutely need them.

soegaard
  • 30,661
  • 4
  • 57
  • 106
  • So kind for your detailed explanation, but your solution (adding 2 `require` line) doesn't work either. If it works on your machine, either you or me must left out sth. version: DrRacket 6.1.1/OS X 10.10.3. – Rahn Jul 26 '15 at 22:26
  • By the way, I've solved this problem already. My confused point right now is how could I make my function at prior order of being search/referenced than the system/standard one in name-duplicate situation. – Rahn Jul 26 '15 at 22:29
  • 1
    @soegaard So every `cons` have a field for properness that gets inherited by the tail at construction? – Sylwester Jul 26 '15 at 23:13
  • @Sylwester Yes. I just checked the source and there is a flag for properness. I thought the length was cached too, but it wasn't. – soegaard Jul 26 '15 at 23:23
1

I don't get any errors running your file in DrRacket. The file you load is probably the old file from the last question. That is the only logical conclusion since none of the files you list here use assoc but the old ones do.

It's not advisable to blend Scheme and Racket. #lang scheme (or #!scheme) is the old name for what is #!racket today and it imports all symbols currently in #!racket and your require statements is in addition to that.

The libraries under rnrs are meant to be used by the R6RS language, thus the first line should be #!r6rs and instead of require you use (import (rnrs base) (rnrs mutable-pairs)). In R6RS you can import except some symbols. Eg. (import (except (rnrs base) cons)) does not import cons but everything else. I didn't use assoc since there is no assoc in (rnrs base) nor (rnrs mutable-pairs) so it was probably from #!scheme (#!racket)

If you plan to use your dictionary as a library you can make it a R6RS library and import it instead of using load.

Also notice that there is a SICP compatibility language in Racket as well. It's based on #!r5rs and most examples from the book works as well!

Community
  • 1
  • 1
Sylwester
  • 47,942
  • 4
  • 47
  • 79
  • I was pasting the wrong code, the improved one. now its the one with problem. I been blending all the scheme liked language (standard). I would try `#!r5rs` but seems it doesn't support REPL? – Rahn Jul 26 '15 at 20:30
  • @Rahn I get the interactive window working with both `#!r5rs` and `#!r6rs`. The perhaps irritating is that evaluated forms in the definitions windows isn't printed as if the program is running, but evaluation in the interaction window does. – Sylwester Jul 26 '15 at 21:23
  • I am sorry I was wrong again. I was about to say that I cannot get the value showed in the window without a `display` – Rahn Jul 26 '15 at 22:21
  • @Rahn I previously made a [question](http://stackoverflow.com/q/19624049/1565698) about that. Easiest solution is to use `display` or perhaps wrap top level in a macro like this: `(define-syntax pbegin (syntax-rules () ((_ expr ...) (begin (begin (display expr) (newline)) ...))))` – Sylwester Jul 26 '15 at 23:08
  • Ah, I just check the link. thanks for all of this. So kind of you. – Rahn Jul 26 '15 at 23:38