Jump to content

Scheme Programming/What defines Scheme?

From Wikibooks, open books for an open world
Scheme Programming
 ← Why Scheme rather than Java, Python or another high-level language? What defines Scheme? A Note on R5RS → 

Whenever we read about Scheme we will often hear the term 'functional programming'; this is true. Scheme was one of the first languages to effectively support this paradigm. What functional programming means is that when we program in Scheme we try to avoid 'side effects'; we don't change the value of variables as much when we've assigned them. In fact, all parts of Scheme that do so end in '!', (called a bang) which already hints that you're doing it wrongly unless you know what you're doing. Scheme is not purely functional though as it does allow side effects, and only discourages them. In Scheme, everything is an expression; a Scheme program is best seen as one long complicated expression; the lexical let block we saw before is an expression as it evaluates to a value. We could have, for instance, used that entire block as a conditional for an if-expression, which is encountered more than what some people would expect.

Another common trait of functional programming is that, of course, functions and procedures are made more powerful than in most other compiled languages. In Scheme, procedures are first-class objects; you can do the same things with them as integers with C. You can pass them to procedures, return them from procedures, store them in variables, or compute them at runtime as the result of an expression.

Scheme also, like most languages, has lexical scope. This means that where procedures are defined in the code defines how they behave, not where they are called. This might seem obvious, because all languages nowadays have lexical scope, but this is important because Scheme was one of the first to stress the importance of lexical scope which gave rise to closures which are nowadays widely implemented in many interpreted languages.

Scheme has no while-loops or for-loops. In Scheme a similar effect is achieved by simply letting a procedures call itself. "Isn't that inefficient?", some would ask, and the answer is, "No!". For Scheme is also characterized by, and pioneered the use of proper tail recursion: whenever a procedures calls itself in such a way that the same effect can be implemented by a loop or iteration, Scheme guarantees that the compiler will rewrite it to a loop at the machine-code level, so you will never run out of stack space or need useless time to call procedures when what you try to do can be rewritten to a loop.

We have already seen in the last section that Scheme can extend its own syntax. This is because Scheme, like any variant of Lisp has one very important property: the language is homoiconic; there is no essential difference between the program itself, and the data that the program manipulates. We already talked about the abundance of parentheses in Scheme source code. This is for a very good reason: the main data structure in Scheme is the so called list (hence: LISt Processor), and Scheme source code itself is also simply a very complicated list. Lists can contain other lists, and since data can be re-ordered, Scheme can easily re-write its own source code and instructions both at compile and runtime, which is how the syntactic extensions are implemented.

But if Scheme was the first to do so many important things, isn't Scheme old? Yes, it is old; it's one of the oldest languages still in use today, which can be seen as both that it's redundant, and that it has proven its worth.