Racket is a descendant of Scheme. How is Racket different than R6RS? What did it add, or take away, or is just different?
I understand that Racket is more than a language, it's a platform for languages. But I'm referring to the main Racket dialect.
Racket is ultimately based on R5RS, and not R6RS and not a strict superset of either. I don't think it can be called 'Scheme' because it's not backwards compatible with any Scheme standard.
Most implementations offer extensions, but are otherwise backwards compatible, of course, the compiler that comes with Racket can also run in R5RS or R6RS mode. Valid R5/6RS Scheme that runs in racket mode may either be rejected, cause runtime errors, or behave differently than it should. With that said, the main points where it is not backwards compatible are:
Racket has no set-cdr! and set-car!, rather set-mcar! which only works on pairs specifically created as mutable.
What Racket calls letrec is called letrec* in R6RS and doesn't exist in R5RS, what R5RS and R6RS call letrec doesn't exist in Racket.
In Racket, a lot of things are self-evaluating which would raise an error in R5RS, most importantly the empty list.
Racket is case sensitive, though R6RS is also case sensitive
Racket treats ( ... ) and [ ... ] as equivalent, R5RS does not, but R6RS does.
There are probably more, but on most other parts racket is a superset of Scheme.
It contains immutable lists, as mentioned above. It also contains a structure system that is a bit cleaner than the R6RS record system. It has an object oriented class and object system. It has native support for design by contract. It has a unit system reminiscent of the ML module system, as well as a module system much like the R6RS module system. I'm sure I've forgotten as many things as I've mentioned.
I'm not sure that the rename was useful as anything other than a marketing gimmick, but racket is definitely a distinct dialect of scheme.
The rationale for the name-change from PLT Scheme to Racket is discussed on the Racket site.
The language specification R5RS on the Scheme programming language is based on consensus between the multiple Scheme implementors. This imply that the language is very stable. It also implies that many useful features are not part of the R5RS standard.
Racket has built upon R5RS and extended it greatly. Some extensions are defined as macros, but some features require the support of the runtime system.
Features in Racket not implementable by macros alone:
delimited continuations (more general than call/cc)
continuation marks
threads
places
ffi
The module and macro system are much more general than the RnRS specification.
Together with #lang reader/language specification makes it possible to define custom languages (with custom syntax) and use them with normal Racket programs.
In a few cases Racket has constructs whose behaviour deviates from R5RS. The most obvious one is making cons construct an immutable pair (mcons constructs a mutable pair). One advantage of a having immutable pairs, is that length now runs in O(1) amortized time.
Racket includes a lot of really nice language constructs not included in R6RS scheme, like "match".
For one big example, Racket lists are immutable by default whereas Scheme's are mutable. Racket also includes a lot of standard libraries (e.g. Web Server) that other Schemes do not.
Related
Using scheme in repl.it, several functions don’t appear to exist, such as modulo, remainder, inc. Am I missing something? Do I need to import some library or anything? I know there are various permutations of scheme but as far as I can tell these are actual base scheme functions.
At the time of writing repl.it uses BiwaScheme v0.6.4 under the hood, which is quite incomplete (the most recent version is v0.7.0, by the way.) It does not fully implement a Revised Report for the Scheme language, because procedures such as modulo or remainder are a standard part of the language. According to their implementation status page:
BiwaScheme has most features of R6RS Base library. The biggest features not implemented are errors and syntax-rules (you can use define-macro instead).
In particular, the arithmetic section is listed as "not yet" conforming to the standard. There isn't much you can do about it, apart from switching to a standard-conformant interpreter. May I suggest Racket?
I'm new to Scheme and I have a background coding mainly in C++/Java, and a bit of Python. I'm doing exercises in Study and Interpretation of Computer Programs, and I've come across this problem:
The book details the structure of the "if" special form as (if (conditional) (consequent clause) (alternative clause)). However, nothing indicates that an alternative clause MUST be included.
In fact, in the exercise I'm currently stuck on (exercise 1.22, for anyone interested), they provide some code that we are supposed to use in creating a procedure that tests for prime numbers within a given range and gives the amount of time taken to find them.
(define (start-prime-test n start-time)
(if (is-prime n)
(report-prime (- (runtime) start-time))))
This did not work, so I modified it slightly:
(define (start-prime-test n start-time)
(if (is-prime n)
(report-prime (- (runtime) start-time))
(display ""))) ;prints nothing
The first version results in "SchemeError: too few operands..." I modified it to have an alternative clause that essentially does nothing, and I'm no longer getting the error when testing the procedure.
I just want to know whether requiring an alternative clause is standard for most Scheme interpreters, or if it's unique to the one I'm using. I AM currently using two different interpreters, because the first one I used does not include the built-in procedures detailed in the book, so I have noticed there are some major differences in Scheme interpreters. But that's as far as I know, and it's been very hard finding useful information through googling.
Any help would be greatly appreciated; I don't like including "do-nothing" procedures.
In most Scheme interpreters if allows expressions without the "else" part, and that's what the standard says, as pointed by #codybartfast in his answer.
I'm only aware of Racket enforcing the rule that if must always have both the consequent and alternative parts, and it's for a very good reason: it'll help you catch the kind of mistakes that happen when you forget to write the "else" part.
Although it's valid to have if expressions without the alternative part, that only happens when we are writing procedural code (like displaying a result in your example), and that's the kind of programming style we want to avoid when using Scheme (as we favor functional programming).
Having said that, if you're absolutely certain that you want to write procedural code, then you should use when, which doesn't have an else part and unlike if, it can have several expressions inside because it has an implicit begin. This will work:
(define (start-prime-test n start-time)
(when (is-prime n)
(report-prime (- (runtime) start-time))))
I understand R5RS/R6RS are effectively the language standards. They say that the alternate clause is optional:
(if <test> <consequent> <alternate>)syntax
(if <test> <consequent>)syntax
Syntax: <Test>, <consequent>, and <alternate> must be expressions.
Semantics: An if expression is evaluated as follows: first, <test> is
evaluated. If it yields a true value (see section 5.7), then <consequent> is
evaluated and its values are returned. Otherwise <alternate> is evaluated
and its values are returned. If <test> yields #f and no <alternate> is
specified, then the result of the expression is unspecified.
But in 2009 (quite recently for scheme), the language steering commitee said:
Alas: Scheme has the unhappy distinction of being the world's most unportable programming language. It is almost misleading to call Scheme a "programming language;" it would be more accurate to characterise Scheme as a family of dialects, all loosely related by the common features of lexical scope, dynamic typing, list structure, higher-order functions, proper tail-recursion, garbage collection, macros, and (some form of) s-expression based lexical syntax.
So although a formal stanard may exist there seems little expectation that any given implementaion will adhere to that standard. E.g. By default Racket requires the alternate clause. (Although Racket can also support a R6RS compliant dialet.)
Personally I use Racket with the SICP language pack to be consistent with the book.
The one-armed if has indeed been standard in Scheme for a while.
Unfortunately the one-armed if allows one to make a mistake - forgetting the second arm is very easy to do. Some implementations added when and unless to reduce the number of mistakes.
See also: Why is one-armed "if" missing from Racket?
There are many differences between Common Lisp and Scheme such as whether functions and variables share a namespace, whether macros are hygienic, and how strongly functional style is preferred; this shows up in some vocabulary differences such as setq vs set!.
But quite a bit of vocabulary is still shared, such as quote and cons.
I'm looking for a full list of vocabulary shared between the languages. Does such a thing exist?
Alternatively, I could make one myself given the vocabulary of each language, i.e. a list of all the known symbols including language primitives, standard library macros and functions. Do those exist for Common Lisp (as in the standard) and Scheme (as in any RxRS, or failing that, any dialect)?
Get all symbols of Common Lisp:
(sort (loop for sym being each external-symbol of "CL" collect sym)
#'string-lessp)
You are looking for this web page:
http://hyperpolyglot.org/lisp
Consider the following s-expression:
((lambda (car) (car (quote (a b c)))) cdr)
In most scheme implementations I've tried, this evaluates to (b c) because cdr is passed to the lambda, which names it car, taking precedence over the primitive implementation of car.
The Little Schemer provides an implementation of scheme written in scheme in chapter 10. That implementation returns a for the above expression, which seems incorrect to me.
It's clear why that implementation behaves that way: the names of primitive functions are treated as *const rather than *identifier here. A *const which is not a number or boolean is rendered as a primitive and this is eventually hardwired to the actual primitives.
I believe that the correct implementation would be to have no special detection of primitive names, but rather to create an initial table in the value function that contains an entry mapping the primitive names to the actual primitive implementations.
My question is: is this a bug in The Little Schemer's implementation of scheme? Is this behaviour well specified in scheme, or was it maybe not well specified in 1974 when the book was written?
Is it a bug?
The question whether it is a bug or not is to establish if the interpreter is supposed to follow Scheme scoping rules. Since you mention the year 1974 it is the year before the first Scheme report was posted, but many of the ideas were probably written about at the time and what was to become Scheme were small interpreters that probably were shared between research students with various subtle differences.
The little Schemer was originally called The little Lisper and worked under a Lisp later to become Common Lisp. When rewriting it to follow Scheme they tried to keep most of the code from earlier so it is likely the interpreter has different features than Scheme.
What the RNRS Scheme reports say
If the interpreter is to conform to the scheme reports it must allow for bindings to shadow the top level bindings. Here is a quote from the R5RS report about Variables, syntactic keywords, and regions
Every mention of an identifier refers to the binding of the identifier
that established the innermost of the regions containing the use. If
there is no binding of the identifier whose region contains the use,
then the use refers to the binding for the variable in the top level
environment
For later reports like the same section in R6RS top level is replaced with "definition or import at the top of the enclosing library or top-level program".
Whenever I try to use srfi/1 functions like fold and reduce in drracket r5rs language I get an null-list? error. After some research I found that it is due to the fact that the function requires and immutable list but gets a mutable one. How do I create immutable list in r5rs or is this srfi/1 not designed with r5rs in mind?
This is basically reiterating what John said, but it definitely does merit a full answer status. (John, I'll be happy to delete this if you copy this answer verbatim...)
The thing about Racket's R5RS language is that it sticks very strictly to just the R5RS language, and therefore comes with nothing beyond that. (SRFIs could be implemented for the R5RS mutable pairs too, but nobody ever came up with the adapted code.)
In any case, the bottom line here is that R5RS is basically useful only in those rare cases where you want to inflict on yourself the pain of writing using just the basic r5rs language -- which means that you basically can't do much useful things. So especially if you want to get a feel for the language, you'll likely want to play with all kinds of things that go beyond the narrow r5rs world -- and for that you should use #lang racket.
It's also better in terms of "just grokking the language", and you can even apply the same lessons to other Scheme implementations baring in mind that they all come with their own extensions. If you use the limited r5rs language, you'll likely to experience the frustration that comes with such a limited language and this can easily overwhelm any positive experience you might have with it otherwise.
Since the language "racket" uses immutable cons cells, the srfi/1 implementation that comes with the system also uses immutable cons cells.
Since cons cell in R5RS uses mutable cons cells, you can't use the builtin srfi/1 with the R5RS language in DrRacket.
That's is however a small problem.
Download http://srfi.schemers.org/srfi-1/srfi-1-reference.scm and save it in the directory where you have your code.
Open it and insert at the first line:
(define (check-arg . more) #t)
Then in your own code, add the line:
(load "srfi-1-reference.scm")
Now srfi/1 is redefines map (and a few others).
Redefined builtin operations is normally done only
on accident, so the default settings in DrRacket
is to throw an error. Therefore click at the R5RS language
at the bottom left in DrRacket. Choose "Choose language...".
Then click the button "Show Details". The remove the tick
by "Disallow redefinition of initial bindings".
Now the following program runs:
(load "srfi-1-reference.scm")
(fold + 0 '(1 2 3))
and produces 6 as expected.