Open main menu

Wikibooks β

Scheme Programming/A taste of Scheme

< Scheme Programming

Like almost every single programming language out there, we might as well start out with the tried-and-true 'Hello, World!' Program. In Scheme, this is spectacularly easy:

(display "Hello, World!")

This is a full-fledged Scheme program, this is also a list with two elements, the first being the symbol display, the second being the string "Hello, World!". Remember, Scheme programs are lists, they are in effect a way in data to describe the structure of a computer program. Though this is a typical first program, it is not a very idiomatic one for Scheme. Scheme is functional, we try to reduce side-effects remember? This is an expression all right, it evaluates to a value we call #<void>, this isn't cheating, it's a value that can be used in another expression, but most interpreters don't display it unlike other values. We could have also done:

"Hello, World!"

This would have our interpreter simply return the source code, it may look similar, the only difference is the quotation marks, but it is in fact very different. The string "Hello, World!" is considered a self-evaluating datum, it evaluates to itself if we throw it onto the interpreter. Most interpreters simply display the value of the last expression evaluated too, unless it's #<void>. Thus, two completely unrelated things have happened. The first example had a termination value of #<void> and had the effect of printing the text Hello, World! on the terminal; the second example had no printing effect at all, the terminal simply showed the termination value of the program, which was the string "Hello, World!". This is very important in Scheme, as said, Scheme programs are in effect just large expressions, and expressions can contain smaller ones. In effect, we've already seen it, for the first program contains the second as a sub-expression in it.

Let's try another example. Commonly, we're introduced to functional programming by defining the factorial function, so let's not do that, and define a function which approaches a derivative :

(define (derivative f dx)
  (lambda (x)
    (/ (- (f (+ x dx)) (f x))

This is a fully fledged Scheme program which again evaluate to #<void>, don't try to understand how it works yet, it's rather complex for the uninitiated. However one thing should be obvious if you remembered your calculus lessons. Scheme uses prefix notation, (2 + 3) though a list is not a valid Scheme expression, the first element of the list must be so-called procedure, Scheme does not differentiate between 'operators' and any other procedure.

This function already packages three important parts of Scheme together: it shows that in Scheme, functions can take other functions as arguments and return a function depending on those functions; and Scheme has lexical scope, i.e., where the function is called in code isn't relevant; only where it's defined. What we have here is a closure, but we'll get to that later on.

This is a valid Scheme program, and it does nothing at all. It just defines the function derivative, and then exits and returns #<void>, we had no use for it at all. It also performed a side-effect before exiting, it bound that function to the symbol derivative. However, the program

(define (derivative f dx)
  (lambda (x)
    (/ (- (f (+ x dx)) (f x))
((derivative cube 0.0000001) 3)

Provided we have a cube function which cubes its argument evaluates to 27.000000848431682, which is pretty close to (* 3 (expt 3 2)), isn't it? Technically it evaluates to two values, #<void> and 27.000000848431682. But for this reason, the interpreter does not display #<void>, else it would be really annoying as customary there are a lot of function definitions in Scheme source.

So, what happened here? Well, we had the expression (derivative cube 0.0000001), this of course evaluates to the function that approaches the derivative of the cube function. 3 is a number, numbers like strings evaluate to themselves. So, the 'outer expression' then evaluates to the result of the derivative of the cube applied to 3.

This is how Scheme is different from most languages, and even most dialects of Lisp. If we have an expression (f x y ... z) then all members of that list are first evaluated in an unspecified order, and if the head evaluates to a function, it is then called with the rest of the elements as arguments. This is why so many types of data evaluate to themselves, and this is why symbols do not. Most languages have variables, Scheme has symbols, and symbols are types of data which evaluate to what they are bound to. + is not the function that adds numbers, it's a symbol that evaluates to that function.

So, hopefully, after this introduction, we've seen some of the things that make the semantics of Scheme different to what's common in many programming languages.

(We have also learned that making quines in Scheme is spectacularly easy.)