The SchemeProgramming Language
University of Washington, Seattle
(C) 1999, Greg J. BadrosAll Rights Reserved
Scheme philosophy
Programming languages should be designed not by piling feature on top of feature, but by removing the weaknesses and restrictions that make additional features appear necessary.
Scheme
- R5RS = Revised5 Report on Scheme(R4RS is the older standard that Dr. Scheme and Guile use)
- Only 50 pages describe the whole language
- Descends from Lisp and Algol
- Strong theoretical foundations
- Becoming a favourite introductory language
C vs. Scheme expressions
Prefix vs. infix
- Infix: 2 * (1 + 2)
- Group explicitly to override precedence
- Operator must be repeated when applied to more than 2 arguments
- Prefix: (* 2 (+ 1 2))
- Lots of parentheses required
- (+ 1 2 3 4 5) ? 15
Nested expressions 1
(* (+ 2 (* 4 6)) (+ 3 5 7))
Nested expressions 2
(* (+ 2 (* 4 6)) (+ 3 5 7))
Nested expressions 3
(* (+ 2 (* 4 6)) (+ 3 5 7))
Nested expressions 4
(* (+ 2 (* 4 6)) (+ 3 5 7))
Finally, do outer multiplication:
Nested expressions 5
(* (+ 2 (* 4 6)) (+ 3 5 7))
Moral: Compute expressions inside-out
Evaluating arguments
So whole expression: (+ 1 2) ? 3
- 1 and 2 are literal numbers that evaluate to themselves!
Types
- Characters: #\A #\space #\newline
- Booleans: #t #f(represent TRUE and FALSE)
More types
- Symbols: + a-list x lambda
- Procedures: #<primitive:+> #<procedure:factorial>
- Lists: (1 "str" #\g factorial +)
- Vectors: #(1 "str" #\g factorial +)
- Ports for I/O #<input-port>
Whats in a symbol?
- Operators are just procedures,so identifiers must allow them
- Valid characters include
+ * / < > ^ ~ _ % ! ? $ : =
- Digits not allowed at start of symbol
Some literalsevaluate to themselves
- Strings: "Hello world" ? "Hello world"
- Vectors: #(a 2 5/2) ? #(a 2 5/2)
Symbols evaluate byvariable lookup
Symbols:x ? 9a-list ? ("ally" "georgia" "elaine")factorial ? #<procedure:factorial>+ ? #<primitive:+>
- So how do we give variables a value?
define special form
- define is not a procedureit is a special form which does not necessarily evaluate all of its arguments
- Allocates space, and binds the symbol x to that space after initializing it to the value:
Lists evaluate byprocedure application*
Lists:(factorial 4) ? 24(+ 1 2) ? 3("ally" "georgia" "elaine") ?Error: procedure application: expected procedure, given: "ally"; arguments were: "georgia" "elaine"
Special forms
define lambda let, let*, letrec
let-syntax letrec-syntax delay
+ any macros (also must be learned)
List evaluation
Do special form processing
Invoke first element as a
the elements as arguments
Creating symbol value
- Symbols evaluate by variable lookup:factorial ? #<procedure:factorial>
- How do we enter a symbol as a value?e.g., suppose we want:x ? foobarWhat do we write?(define x )
Suppressing evaluation
Error: reference to undefined identifier: foobar
- foobar is evaluatedSince symbols are evaluated by variable lookup we get the error
- But we want the symbol foobar
- Solution: must suppress the evaluationof the symbol foobar
quote special form
(define x (quote foobar))
Gets evaluated to initialize space x is bound to
Because quote is a special form,foobar does not get evaluated when
evaluating the list (quote foobar).
quote just returns its argument un-evaluated!
Quoting
- is shorthand because quote is so usefulfoobar ? foobar
- Suppose we want a value that is a list?(1 2 3) ? ERROR (1 is not a procedure)(1 2 3) ? (1 2 3)
Forcing evaluation with eval
- eval evaluates its single argument
- eval is implicitly called to evaluate each expression you enter into the repl(read eval print loop)
The Lambda Calculus
- Typical function in mathematics:f(x,y) = 2x + y
- Alonzo Churchs lambda calculus lets us talk about un-named (anonymous) functions:
arbitrary namesof arguments
Creating procedures withthe lambda special form
(+ (* 2 x) y)) ? #<procedure>
((lambda (x y) (+ (* 2 x) y)) 4 5) ? 13
A moment for syntax
((lambda (x y) (+ (* 2 x) y)) 4 5)
([lambda (x y) (+ (* 2 x) y)] 4 5)
;;; can use [ and ] as grouping constructs
;;; in some versions of Scheme
;;; (including Dr. Scheme)
Naming a procedure
- No new rule!(define f [lambda (x y) (+ (* 2 x) y)])(f 4 5) ? 13
- Remember list evaluation rule:we evaluate f by doing variable lookup!
Shorthand forprocedure definition
(define f [lambda (x y) (+ (* 2 x) y)])
Procedures vs. variables
- (define f 3)f ? 3(f) ? ERROR
- (define (f) 3)f ? #<procedure:f>(f) ? 3
which is a procedure that,
Conditionals:if special form
(if TEST TRUE-EXPR FALSE-EXPR)
- if is an expressionevaluates to either TRUE-EXPR or FALSE-EXPR depending on the value of TEST
C vs. Scheme
C vs. Scheme
Evaluates to either#<procedure:subtract>
eq? proceduretests for identity equality
- Procedures ending in ? are predicates they return a boolean value, #t or #f
- Symbols are only useful as identifiersand for eq? comparison testing
Recursion
[* n (factorial ( n 1))])) ; inductive step
Procedure is calling itself
Linear recursive process
(* 4 (* 3 (factorial 2)))
(* 4 (* 3 (* 2 (factorial 1))))
Adapted from:Abelson & Sussman, Fig. 1.3
Lists are made of cons cells
- Lists are a recursive data structure!
(a b c d) is shorthand for:
cons cellseach holds a pair of values
cons cells and thecons procedure
List syntax shorthand
(cons a (cons b (cons c (cons d ()))))
(cdr x) ? (b c d) ; another list!
Beware the arrow:Another look at lists
car, cdr, and friends
(car (cdr x)) ? b (cadr x) ? b
(car (cdr (cdr x))) ? c (caddr x) ? c
All combinations of up to four a/d characters are standard; e.g.:
caaaar, cddddr, cdadar, cdar, cadadr, etc.
Nested lists
(* (+ 2 (- 4 6)) (+ 3 5 7))
Do not try this at home
- Rely instead on recursive processing of the data structure
our-list-ref procedure
(define (our-list-ref items n)
"Return element N of list ITEMS."
(our-list-ref (cdr items) (- n 1))))
our-list-ref traceLinear iterative process
(our-list-ref '(a b c d) 3)
(our-list-ref '(b c d) 2)
- No expansion and contraction!
base case n=0,so return (car (d))
Contrast theinductive steps
[our-list-ref (cdr items) (- n 1)]
- factorial recurses and the result is used in further computation:(* n )
Tail-recursion
our-list-ref is tail-recursive
- Recursive calls return valueis the final result
- Thus, the recursion can be replaced with iteration (i.e., a goto the top of the procedure with variables re-bound)
- Standard Scheme implementations are required to eliminate tail recursions!
our-list-ref tail recursion
(define (our-list-ref items n)
(our-list-ref (cdr items) (- n 1))))
Re-binding is NOT assignment
Iterative version offactorial
(define (fact-iter product counter max-count)
(if (> counter max-count)
[fact-iter (* counter product)
Iterative factorial trace
(factorial 4) ;; product counter max
counter > max, so terminate
Nested procedure defines
[define (iter product counter)
[iter (* counter product)
Factoring outcommon sub-expressions
f(x,y) = x(1+xy)2 + y(1-y) + (1+xy)(1-y)
let special form
(+ [* x (square a)] [* y b] [* a b])))
Scope is visibility
(+ [* x (square a)] [* y b] [* a b])))
a ? ERROR (a not visible at top level)
Scope of a and b only in this block
let bindings happenin parallel
(let ([x y] [y x]) (list x y)) ? (b a)
- Remember, it is still not assignmentonly re-binding
Bad let bindings
(let ([x 1] [y (+ x 1)]) (list x y))
- So what if we do want y to be computed in terms of x?
Reference to undefined identifier
let* special form
(let* ([x 1] [y (+ x 1)]) (list x y)) ? (1 2)
(let ([y (+ x 1)]) (list x y)))
Syntactic sugar fornested lets
More about conditionals:cond special form
(if TEST EXPR-TRUE EXPR-FALSE)
- cond evalutates to EXPR-K, where K is the lowest of the TEST-Ks that evaluates to #t
(cond [TEST-1 EXPR-1] [TEST-2 EXPR-2] [TEST-N EXPR-N]
[else ELSE-EXPR]) ; or [#t ELSE-EXPR]
cond example
(cond [(> number 0) positive]
Short-circuiting and, orspecial forms
(and (> 1 0) (> 2 3) (/ 5 0)) ? #f
(or (> 0 1) (> 2 1) (/ 5 0)) ? #t
#f, so never evaluates this
#t, so never evaluates this
Boolean values & and, or
- All values except #f are treated as #t in conditionals
- and evaluates to the last value in sequence if it succeeds (not necessarily #t)(and (+ 2 3) (- 3 1)) ? 2
- or evaluates to the first true value in sequence if it succeeds (not necessarily #t)(or (> 4 5) a (/ 5 0)) ? a
Procedures are first-class values
- lambda special form returns a procedure
- Procedures can take procedures as arguments
- Procedures can return procedures
- Procedures are treated no differently from other kinds of values
(e.g., numbers, lists, strings, symbols, etc.)
map, a higher-order function
(define (negation x) (- x))
(map negation (1 2 -5 7)) ? (-1 -2 5 -7)
(map + (1 2 3) (4 5 6)) ? (5 7 9)
(map [lambda (x) (+ x 2)]
- A higher-order function is one that operates on other functions
two lists, so give two arguments to each + call
the list of return values
Filter procedure
(filter number? (a 4 "testing" +)) ? (4)
(filter [lambda (x) (>= x 0)]
(filter [lambda (x) (>= x 2)]
Typechecking predicates
(pair? (a b c d e f)) ? #t
(pair? ()) ? #f (pair? 6) ? #f
(null? ()) ? #t (null? 0) ? #f
Procedure factories
(filter [lambda (x) (>= x 0)]
(filter [lambda (x) (>= x 2)]
(define (greater-than-n?? n)
Building procedures
(define (greater-than-n?? n)
greater-than-n?? ? #<procedure>
(greater-than-n?? 2) ? #<procedure>
((greater-than-n?? 2) 3) ? #t
(define greater-than-2 (greater-than-n?? 2))
Dr. Scheme Graphics
(define v (open-viewport ))
(define red (make-rgb 1 0 0))
((draw-pixel v) (make-posn 100 120) red)
(define draw-on-v (draw-pixel v))
(draw-on-v (make-posn 100 120) red)
Returns a procedure that is specializedfor drawing a pixel on the viewport v
lambdas and closures
(define (greater-than-n?? n)
- which n? Looks like an undefined reference!
- lambda remembers the value n had when the procedure was createdit closes over its environment and gives a closure
lambdas andtheir environments
(define (greater-than-n-by-k?? n k)
(lambda (x) (>= x (+ n k))))
(greater-than-n-by-k?? 2 5) ?
Free variables andLexical Scoping
(define (greater-than-n-by-k?? n k)
(lambda (x) (>= x (+ n k))))
(define pred (greater-than-n-by-k?? 2 5))
Dynamic Scoping
(define (greater-than-n-by-k?? n k)
(lambda (x) (>= x (+ n k))))
(define pred (greater-than-n-by-k?? 2 5))
Lexical vs. Dynamic Scope
- Lexical
- Fewer surprises
- Easier to compileall mentionsof a variable refer to the same variable
- Dynamic
- Historical LISPsconsidered an implementation bug by McCarthy
- Can result in confusing name capture
(Sometimes (but rarely) this is what you want)
Lexical scopeand variable hiding
(display x) ; writes "hello"
(display x)) ; writes (1 2)
(display x)) ; writes "hello" again
When the arguments dont fit
- Suppose we have args ? (1 2 3) and we want to compute the sum:(+ args) ? Error; expects args of type number
- We need a list with + as first element,then the elements of args: (+ 1 2 3)
- Create the list and evaluate it:(cons + args) ? (+ 1 2 3)(eval (cons + args)) ? 6
A more direct approach:the apply procedure
(apply [lambda (x y) (+ x y)] (1 2))
Procedure arity
- (define (proc0) foo)(arity proc0) ? 0
- (define (proc1 arg) foo)(arity proc1) ? 1
- (define (procn . args) foo))(arity procn) ? #(struct arity-at-least 0)
- (define (proc reqd1 reqd2 . rest) foo))(arity proc) ? #(struct arity-at-least 2)
Rest arguments
(define (skip-first ignore . rest) rest)
(skip-first 1 2 3) ? (2 3)
(define (skip-first . args) (cdr args))
(define skip-first (lambda (. args) (cdr args))Error! Cant put . as first element in list
(define skip-first (lambda args (cdr args)))
Controlling evaluationinside of lists
(1 a (+ 2 3)) ? (1 a (+ 2 3))
(list 1 a (+ 2 3)) ? (1 "Hello" 5)
(list 1 a (+ 2 3)) ? (1 a 5)
`(1 a (+ 2 3)) ? (1 a (+ 2 3))
`(1 a ,(+ 2 3)) ? (1 a 5)
`(1 ,a ,(+ 2 3)) ? (1 "Hello" 5)
quasiquote and unquote
- (quasiquote (?)) ? `(?)Only evaluate parts of ? that are unquoted
(quasiquote (1 a (unquote (+ 2 3))) ? (1 a 5)
Forcing and suppressingevaluation are fundamental
- Not Schemes faultit just gives you finer control!
- C/C++ lack such complexity in the general case because they are more restricted languages
- Analogous to dereferencing or taking address of variables when dealing with pointers
Comparisons
- C/C++:(4 == x) && (\n == ch)
- Scheme:(and (= 4 x) (char=? ch #\newline))
- Other comparison procedures:string=?eq? eqv? equal?
eq?, eqv?, equal?
(eq? a a) ? #t (eq? a b) ? #f
(eq? 9 9) ? #t (eq? 1234567890 1234567890) ? #f
(eqv? 9 9) ? #t (eqv? 1234567890 1234567890) ? #t
(equal? "foo" "foo") ? #t
(eq? (list 1 2 3) (list 1 2 3)) ? #f
(equal? (list 1 2 3) (list 1 2 3)) ? #t
More eq?, eqv?, equal?
- Values can look the same,but be different objects
- eq? tests for object-identity equality
- returns #t only when they are same object
- symbols are interned to make them the same
Objects/values in theScheme Heap
Do not use eq? for numbers
- Some numbers compare eq? to themselves, others do not
- Answer depends on how Scheme stores numbers (implementation-dependent):
Making new lists
- Already saw how to get a list with a new first element:(cons 1 items) ? (1 2 3)items ? (2 3) ; items is unchanged
- How can we get (2 3 4)?(cons items 4) ? ((2 3) . 4) ; wrong!(append items 4) ? (2 3 . 4) ; closer(append items (4)) ? (2 3 4) ; Yes!
Sharing of list structure
(define longer (cons 1 items))
longer ? (1 2 3)items ? (2 3)
(eq? items (cdr longer)) ? #t
append mustduplicate the list
(define longer (append items (4)))
List surgery
set-cdr! and set-car!
- Change what a cons cell contains
- They side-effect the values passed in!
- Exclamation point (!) tells you that an argument is being altered
- Non-functional featureuseful mostly for efficiency
Assignment
set! procedure
Other side-effects
- display procedureinput/output in general(display 1) ? #unspecified
- (define x #\A) ? #unspecified
- (iconify-window (get-window)) ? #unspecified
Sequencing andthe begin special form
(hw) ? #unspecified ; prints "hello world"
(display "hello ") 2 "foo" (display "world")
"last"))) ? "last ; prints "hello world"