Lambda Forever!

Frequently Asked Questions
about Racket and Scheme

CS 3540

Programming Languages
and Paradigms

Introductory Note

Why do you talk about Racket and Scheme?

Racket is a fork of Scheme, the simple language at the core of this course for many years. Scheme was created primarily as an experiment in understanding how programming languages work. Racket retains its basic favor, but it also adds many, many features that make the language more relevant in the 2010s. (It also changes a few details that make it more useful to modern programmers.)

We use Racket in this course because it is more modern, has better tools, and a thriving community doing cool work. But there are so many valuable references from the Scheme world that it's hard to talk about Racket in this course without referring to Scheme.

Questions about Specific Details of Racket

Where did the names of the procedures cons, car, and cdr come from?
These names are an homage to the first implementation of Lisp, on an IBM 704. A word on the 704 consisted of two parts, the address register and the decrement register. car and cdr were macros for accessing the contents of the address register and the contents of the decrement register, respectively.

Scheme modernized many things about Lisp, but kept these names for reasons both of culture and convenience. You can read more about them, including the convenience reasons and even the IBM 704 macro code for the car and cdr macros, at the Wikipedia pages for car and cdr.

Racket modernizes Scheme in many ways, including by adopting Common Lisp's names for these fundamental procedures: first and rest.

cons is the sort of abbreviation that most programmers love. It is the procedure for constructing lists. More specifically, cons allocates a single two-address cell, which we usually just call a cons cell. In this sense, cons is an inverse of car and cdr.

Are Racket identifiers case-sensitive?
Yes. However, Scheme identifiers are not. car, Car, and CAR all name the same procedure. One effect of this feature is that, even in Racket, we rarely use embedded capitals when naming things, but rather punctuation characters -- usually the hyphen. Instead of LastElement, we would say last-element.

Does Racket provide operators for programming with side effects?
Yes. However, side effects are not part of the functional style of programming, so we do not reach them until much later in the course. We have seen and used one operator with side effects at the top level: define, which binds a name to a value. Because define expressions are evaluated for their side effects, Dr. Racket does not print or return a value for them. Later, we will see set!, which gives us the ability to change the value of an existing binding, and begin, which gives us the ability to sequence expressions. Finally, input/output procedures such as read, write, and print have side effects, too.

The list is Racket's basic aggregate data type. Can we make multidimensional lists?
Yes. A list can be a list of any values, including other lists. We will take advantage of this flexibility often, with lists containing other lists of arbitrary length.

Is there a list of all of Racket's primitive procedures?
I do not know of a simple list of all the procedures defined in Racket's base language. However, the index to the Racket language reference includes all the procedure and symbol names in the language. Racket is a large language, so this list is quite long. For this course, we can often use Chapter 6 of Revised Report on the Algorithmic Language Scheme, as a quick reference.

How can I tell if a procedure is primitive or not?
This can be tough when reading The Little Schemer, because the authors sometimes use one of their own procedures to define the behavior of a built-in Scheme procedure.

To determine if a procedure is a Scheme primitive, look into the Scheme language definition under the appropriate category. For example, if you would like to know if add1 is a Scheme primitive, go to the section of the language definition on numbers to see if it is defined. (It's not.) Or if you would like to know if rember is a Racket primitive, go to the section of the language definition on lists to see if it is defined. (It's not.)

You have another way to determine if a procedure is built into Racket: evaluate the name of the procedure in Dr. Racket. If you have not defined a procedure with that name, and the name evaluates to a compiled #<procedure:...>, then the procedure must be a Racket primitive.

          > first
If it causes an error because the name is not bound to a value, then it's not.
          > foo
          ... foo: undefined;
            cannot reference an identifier before its definition

Questions about Racket as a Programming Language

Are Racket procedures defined in libraries, as in Java?
Not the primitives; they come as a standard part of the language interpreter, just like Java primitives do. Of course, people do create their own libraries of procedures and load them into the Racket environment. In fact, you will find that this is exactly what we do when we write Racket programs!

Programming language researchers have long been working on module systems that allow programmers to share libraries of procedures more easily. Dr. Racket provides a well-developed, full-featured module system.

How has Scheme avoided bloat and creeping feature-itis?
The language was originally designed as a minimalist language: What is the smallest set of features we need to talk about all the programming languages ideas that matter? As it moved from the lab out into the world, new operators were added to Scheme, making it more convenient to use. But it is still a minimalist language, and the community of users that guides its evolution is supportive of this mindset. As a result, features get added to Scheme only when there isn't already a reasonable way to implement the idea consistently using language features that already exist.

Racket extends Scheme in radical ways, but modularizes extensions in separate languages. We will talk a bit about this later in the semester.

What are some of the restrictions that result from Scheme being so small?
It does not have 1001 primitives to make your life easier from the outset. The language does not include primitives for loops (at least commonly recogniziable ones), threads, graphics, or even classes and objects.

Does that mean we cannot write programs using these ideas? No. Scheme is more than powerful enough to implement all of these ideas, plus ideas more complex than you are used to using. It simply means that they we will have to find an implementation of the idea, or roll our own. For example, I know of a couple of canonical implementations of object-oriented programming primitives for Scheme. If I want to do full OOP right now, I would use one of them. Of course, as you'll see later in the semester, we can implement OOP concepts on our own in relatively little code.

What are some of Scheme's weak points?
Being small is a weak point, if you are programmer who wants a language that provides primitives for a particular kind of program at your fingertips. It's awfully nice to walk up to Java and find a full blown GUI programming package defined as part of the language.

So, people write their own and share.

Where is the weakness in that? Portability is sometimes a problem. Unless a programmer implements his library using only standard Scheme, there is a risk that you won't be able to use his library on your system. It may sound easy to use only standard Scheme, but in the heat of implementing a big package it is awfully tempting sometimes to use some of the non-standard features available in your environment. (Dr. Racket is a siren of this sort.)

What else? Scheme is different than most other languages programmers use. It looks different, with all those parentheses and lambdas. It feels different, with its emphasis on functional programming and no side effects. Unfamiliarity can be a weakness, because it makes the language less immediately accessible to new programmers. It takes more effort to learn, because it changes the programmer's habits.

The use of function composition and higher-order procedures (we'll talk about that soon!) can result in "dense" code -- a small amount of code that implements a lot of different ideas. Unlike Java and Ada, there isn't much dead space or many dead characters. When a programmer is used to roomier code, Scheme code can intimidate them.

The good news is that these weaknesses can be overcome by time and effort. The unfamiliar can become familar. And on the other side is a productivity and a sense of power that is hard to imagine when slogging away in Java, C, or Ada.

Does Racket have the same weak points?
Racket addresses Scheme's weaknesses head-on. It offers Scheme's level of productivity with features that make it an able alternative to just about any other language you can think of.

What is the lambda calculus?
From an on-line introduction to the lambda calculus at Monash University:
The lambda calculus is a formal mathematical system devised by Alonzo Church to investigate functions, function application, and recursion. It has influenced many programming languages but none more so than the functional programming languages. Lisp was the first of these although only the "pure" Lisp sublanguage can be called a true functional language. Haskell, Miranda, and ML are more recent examples. Lambda calculus also provides the meta-language for formal definitions in denotational semantics. It has a good claim to be the prototype programming language.

The lambda calculus is in many ways the prototype for all programming languages. Like the Turing machine, it is capable of implementing any computable function. Unlike the Turing machine, it focuses on the transformation rules that implement a function, rather than the underlying machine implementation. Hence it lies closer to the level of software than the Turing machine.

You can read a lot more about the lambda calculus at the Monash University site. You shouldn't be surprised later in the course when we use and interpret a subset of Scheme that is remarkably similar to Church's calculus.

What was the original purpose of Scheme?
Steele and Sussman "discovered" Scheme in an effort to design a minimal programminmg language. From Steele's foreword to Scheme and the Art of Programming:
The original Scheme ... was a Swiss army knife. Hewing close to the spirit of Alonzo Church's lambda calculus, it had just one of anything if it had one at all.

Because Scheme is minimal whereas many of the languages you know well are decidely not (C++, Ada, even Java), it gives us a way to talk about the trade-offs between small and large languages. Because Scheme is minimal, it leaves us a lot of freedom to talk about most every language feature, what they are useful for, how to implement them, and what trade-offs they impose on us. Because Scheme is minimal, it is small enough for us to master it well enough to implement language interpreters in it.

Questions about Racket in the World

Why use Racket to study programming languages?
If Scheme has one niche to fill in the universe, it is as a tool for studying languages. Scheme defines only a minimal number of features, providing the atoms out of which we can build and understand the molecules that are programming language features. Racket extends Scheme but retains the features that make it a perfect vehicle for studying programming languages.

One of the reasons that Racket works so well for studying languages is that symbols and lists are two of its primary data types. In any lanhguage, once you move past the surface syntax, a program can be thought of as a tree of symbols and values. Trees are easily represented as lists of lists in Racket, and symbols are first-order values.

Finally, as we will learn soon, programs are recursively defined data structures, and using a functional programming style exposes this in the interpreters we write. Even more, functional programming takes advantage of the recursive nature of programs to produce small, simple, and relatively easy to understand interpreters.

What can Racket do as a programming language?
Anything that any other "general-purpose" language can do.

This is true in a theoretical sense, because Racket -- and even Scheme -- is "universal": any computable function can be computed in it.

But Racket is universal in a practical sense, too. People use Racket to write business data processing software, network control software, AI programs, and even computer games.

The Racket team eats its own dog food: Racket and all of its tools, including Dr. Racket, are written in Racket even the web server that supports

Of course, like any language, it is better suited for some tasks than for others. For example, Scheme may not be your first choice for "bit twiddling" sorts of programs, because most of its primitives are rather high-level and it does not connect you directly to any particular machine architecture. Of course, the beauty of Scheme is that you can extend it to include any feature that you want but that basic Scheme does not have -- or you can rather easily write an interpreter for a more suitable task-specific language. That is The Racket Way.

Is Scheme used for any "real world" programming?
Yes, there are deployed, operational, "real" Scheme programs working out in the world.

But. If your question is meant to imply that use in "real" applications is necessary before you consider the language worthy of the effort to learn, then my (only partly) tongue-in-cheek answer becomes, "I don't care". Not every language or skill you learn is directly applicable to your job tomorrow. Our goal at the university is for you to learn ideas that last for a while. No one can predict with any certainty what language -- or even kind of language -- you will be using in two, five, or ten, years. But I can tell you what principles will underlie their design, implementation, and use.

Learning Racket will help you to learn those principles. It will also help you to learn functional languages, and languages with functional features, more easily later. Practice learning a new language is worth the effort if only because it makes learning the next one easier! (Of course, if none of the tools and languages that you learn as an undergraduate are used in the "real world", then you may have cause to complain...)

Then again, if you believe Paul Graham, you should be using Racket or some other Lisp precisely because it is the most powerful language around.

Why have I never heard of Racket or Scheme before? Will it last?
You have lived a short life. Appreciate the opportunity to see the beauty of Racket before heading off into a world filled with Curly Brace People.

Racket forked Scheme. Scheme forked Lisp. Lisp dates to 1958. The first compiler was written in 1962. Scheme dates to the fall of 1975. The code and tool base of Racket date to the mid-1990s. Lisp evolves and survives.

Eugene Wallingford ..... ..... January 9, 2017