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.
Related
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".
Although LISP has some of the most simple syntax I've seen, I am still confused about the fundamentals. I've done research, and I've come to the conclusion that there are two datatypes: "atoms" and lists. However, I have also come across the term "S-expression", which seems to describe both atoms and lists. So, what exactly is an S-expression? Is it a datatype? In addition, I am not sure how to distinguish function calls from data lists in LISP. For example, (1 2 3) is a list, while (f 2 3) could be some function. But how am I supposed to know whether f is a function name or some datatype? Since lists and functions use the same syntax, I am not sure how to differentiate between the two. Finally, most importantly, I need a mental model for thinking about how LISP works. For example, what are the fundamental datatypes? What are the built-in procedures used to do things with the fundamental datatypes? How can we see data and procedures as distinct? For example, in Java, instance variables at the top of classes are used to represent data, while methods are the procedures that manipulate the data. What does this look like in LISP?
(I'm new, so I'm not sure if this question is too broad or not)
I second the recommendations for LispBook and Practical Common Lisp. Both great books. Once you have understood the basics, I really, really recommend Paul Graham's "On Lisp".
To guide you toward answers on your specific questions:
Data types: Lisp has a rich set of data types (numeric types such as integer, rational, float - character and string - arrays and hash-tables - plenty more), but in my opinion, assuming you are already comfortable with integer and string, you should start by reading up on symbol and cons.
symbol: Realise that a symbol is both an identifier and a value. Understand that each symbol can "point to" one data value and, at the same time, "point to" one function (and has three other properties that you don't need to worry about yet)
cons: Discover that this magical thing called a 'cons cell' is just a struct with two pointers. One called "the Address part of the Register" and the other called "the Decrement part of the register". Don't worry about what those names mean (they're no longer relevant), just know that the car function returns the Contents of the Address part of the Register, and cdr returns the Decrement part. Now forget all of that and just remember that modern Lispers think of the cons cell as a struct with two pointers, one called the car and the other called the cdr.
Lists: "There is no spoon." There is no list either. Realise that what we think of as a list is nothing more than a set of cons cells, with the car of each cons pointing to one member of the list, and the cdr of each cons pointing to the next cons (except for the last cons, whose cdr is "the null pointer" nil). This is vital to understand list manipulation, nested lists, tree structures, etc.
Atoms: For now, think of an atom as a number, a character, a string or a symbol. That's enough to get you started you can dig deeper into the details later.
S-expressions: An s-expr is defined as "either an atom, or a cons cell pointing to two s-expressions". Don't worry about what is or isn't an s-expr for now, but checkout wikipedia for a brief intro
Lists vs function calls: Man, oh, man! I struggled with this when I got started! But it's actually quite easy with a bit of practice: "Five times two" is just an English phrase, right? But if I asked you to "evaluate" it, you would probably say "ten". "1 + 2" is a mathematical expression, but a mathematician would evaluate it to 3. "5" is just a number, but if you type it into a calculator and press "=", the calculator will evaluate it to the answer 5. In Lisp (+ 1 2) is just a list. It is a list containing the symbol +, the number 1 and the number 2. BUT - if you ask Lisp to evaluate that list, it will call the function +, passing it 1 and 2 as parameters. A large part of getting comfortable with Lisp is learning when and where s-expressions are evaluated, and where they aren't. For now - anything you type into the REPL will be evaluated, parameters to function calls will be evaluated, parameters to macro calls will probably not be evaluated, you can use quote to prevent evaluation. This will become easier with practice.
Essential functions: Although I said the "list" doesn't really exist ("Its conses all the way down, Mr. Fry"), learn the list based stuff first, car, cdr, cons, list, append, reverse and the applicative stuff mapcar, mapcons, apply. These are a great way to start thinking in both lists and functional programming.
Classes, member data and methods: I recommend leaving object orientation for later. First learn the basics of Lisp. Fight the urge to worry about data encapsulation, access control and polymorphism until you have become friends with the language itself. When you are ready, read "On Lisp", Chapter 25 will walk you through adding object orientation to Lisp yourself, showing how Lisp really is the programmable programming language. The book will then introduce you to CLOS, the standard object-orientation system built into Common Lisp. Get to know CLOS, but certainly browse around for other OO libraries. Lisp is the only language I know where you can actually choose how your want the language to implement OO.
I'm going to stop here. Get comfortable with the first 8 concepts above and a strong mental model will will have sorted itself out.
Everything that is self evaluating is data. eg 2, "hello".
Everything that is quoted is data. eg '(f 3 4), 'test, or the longhand version (quote test)
Everything else that is fed to the REPL needs to be code. eg (f 3 4) is code. It's s-expression and indistinguishable to the quoted data above, but it isn't quoted so it has to be code.
There are some special forms like if, let, lambda, defun, ... that you just need to learn how works. How do you know that if isn't a method in a C dialect like Java or C#? You just have to know them by heart.
You also need to know some essential functions. Usually they are introduced in each and every tutorial together with the special forms. I recommend you read Land of Lisp and Practical Common Lisp for Common Lisp and Realm of Racket and How to design programs for Racket. For pure Scheme I recommend The wizard book (SICP). Don't do them all at the same time. Learning the other ones are easy when you have learned one good enougn.
Now, learning a Lisp is more difficult than learning another C dialect. It's because it is a totally different language with different ways to do stuff. Eg. you won't find for or while loops and variables don't have types but the objects they refer to have. You need to learn how to program almost as if you didn't know a C dialect. (Actually, knowing a C dialect might make the learning harder)
Good luck!
An "S-expression" is 'an atom, or a list of S-expressions' (there are some more complications here, I'll skip those for now, basically boiling down to something called "read macros", where a difference between the textual representation and the internal representation can be done, to simplify human writing).
(1 2 3) is a list. But, (f 1 2 3) is also a list. However, in some (most?) circumstances, it can also be a function call (rather than a function).
I think you mostly have the syntax down, the rest is semantics (in a very technical sense). At this point, I would point you at some good reading material, there's plenty of good books around (I started with an earlier edition of Winston-Horn's "Lisp".
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.