Professional Documents
Culture Documents
Ken Dickey
WHAT IS SCHEME?
So what does Scheme look like? Well, it looks a lot like Lisp.
Don't let this put you off. We can change how it looks (and will
in a future article). But what is important are the concepts
behind it and what you can say with it. So let me make a few
comparisons between Scheme and, say C. You already know that
Scheme is prefix and C is infix:
Scheme C
(+ 2 3 4) (2 + 3 + 4)
(f x y) f(x, y)
In Scheme, all data types are equal. What one can do to one data
type, one can do to all data types. This is different from most
languages which have some data types that can do special things
and other data types which are specially restricted. In most
languages numbers have special rights because they can be used
without having names (imagine having to name each number before
using it!). Functions are specially restricted because they
cannot be created without a name.
(sq 9) -> 27
then (* 2 3 4)
else (+ 2 3 4)
(add3 7) -> 10
((curried-add 3) 7) -> 10
Rubrics:
Assignment: (set! x 5)
Booleans: #t #f
Rubrics:
- A vector's contents can be any data objects.
(exact? 3) -> #t
(integer? 2) -> #t
(real? 2) -> #t
------
;; selector function
------
;; message passing
(define (PAIR (a b)
(lambda (msg)
((second) b)
) ) )
------
;; alias
------
;; pure functions
Now that we are warmed up, let's take a look at ye ol' factorial
function on integers.
(define (fact n)
(if (< n 2)
1
(* n (fact (sub1 n)) ;; (define (sub1 n) (- n 1))
)
(define (cfact n k)
(if (< n 2)
(k 1)
(cfact (sub1 n)
(lambda (result) (k (* n result))))
) )
(cfact 2
(lambda (result) (identity (* 3 result)))) ;; k'
which is
(cfact 1
(lambda (result^) ;; k''
((lambda (result) (identity (* 3 result))) ; fun k'
(* 2 result^)) ; argument to k'
->
->
->
(identity (* 3 (* 2 1)))
->
(* 3 (* 2 1))
The point of this is not that we can take something simple and
make it look bizarre. So why did I do it? The point is that we
can take control which is typically hidden on the stack at run
time and study and manipulate it as source code. This lets us do
some interesting things. For example, we can ignore the
continuation passed in and use another one. If we are writing a
game or an AI search routine or a mathematical routine which
converges on a result, we can monitor our progress. If our
progress is slow, we can save our continuation--"where we are
now"--and try a different approach. If the newer approach is
even worse, we can go back to our saved continuation and try
again from there. We can of course save our attempt at doing
better to try again just in case it really was better...
(define (cfact n k)
;; lexical scope means we can nest functions
(define (ifact x acc k^)
(if (< x 2)
(k^ acc)
(ifact (sub1 x) (* x acc) k^)
) )
(ifact n 1 k)
)
(define (cfact n k)
(ifact n 1)
)
(define (fact n)
(ifact n 1)
)
-----
;;========================MACROS=================================
glerph glop"
"ugly glop"
#define mean( a, b ) a + b / 2
becomes
x+y+x*y/2
x + y + ((x * y) / 2)
...
swap( temp, x )
becomes
...
...
SOME SOLUTIONS
So (let ( (a 1) (b 2) ) (+ a b) ) rewrites to
( (lambda (a b) (+ a b)) 1 2 )
(macro LET
(lambda (form)
(cons (cons 'lambda
(cons (map car (cadr form))
(cddr form)))
(map cadr (cadr form)))))
And here is the Scheme version using pattern maching with error
checks:
(define-syntax LET
(syntax-rules ()
( (let ( (<var1> <init1>) ...) <exp1> <exp2> ...) ; before
; rewrites to
((lambda (<var1> ...) <exp1> <exp2> ...) <init1> ...) ; after
) )
)
Scheme can remain such a small language because one can extend
the syntax of the language without making the compiler more
complex. This allows the compiler to know a great deal about the
few (7) basic forms. Most compiler optimizations then fall out
as general rather than special cases.
(define-syntax OR
(syntax-rules ()
((or) ;=>
#f
)
((or <test>) ;=>
<test>
)
((or <test1> <test2> ...) ;=>
(let ( (temp <test1>) )
(if temp temp (or <test2> ...))
) )
) )
(define-syntax AND
(syntax-rules ()
((and) ;=>
#t
)
((and <test>) ;=>
<test>
)
((and <test1> <test2> ...) ;=>
(if <test1> (and <test2> ...) #f)
))
) )
Examples:
(define A 37)
The second form is known as NAMED LET and allows recursion within
the let form. For the example above, the call to LOOP acts as a
goto which rebinds the <name>s to fresh values and "starts over
at the top of the loop". Note that this is a functional
construct: there are no unbound variables introduced and no
assignment is used.
(define-syntax LET
(syntax-rules ()
Example:
(define A 37)
(let ( (a 2) (b (+ a 5)) ) (* a b)) ;=> 14
(define-syntax LET*
(syntax-rules ()
Example:
(letrec ( (EVEN?
(lambda (n)
(if (zero? n) #t (odd? (sub1 n)))))
(ODD?
(lambda (n)
(if (zero? n) #f (even? (sub1 n)))))
)
(define-syntax LETREC
(syntax-rules ()
(define-syntax COND
((cond) ;=>
#f
)
(define-syntax CASE
(syntax-rules ( else )
((case <key>)
<key>
)
<Side effects>
<Introduced names>
<Macros look like functions but are not {ex: lambda*}>
=============
LOOKING FURTHER
;;============================================================
BOOKS ON SCHEME
=====
STANDARDS:
======
Chez Scheme and MacScheme are probably the best known commercial
implementations, but there are a large number of experimental and
teaching implementations as well. Several are available via ftp from
the Scheme Repository on nexus.yorku.ca [130.63.9.1] under pub/scheme.
Contact: Ozan Yigit: oz@nexus.yorku.ca. Not all conform to the recent
IEEE/ANSI standard.