The SchemeProgramming Language
University of Washington, Seattle
(C) 1999-2000, Greg J. Badros and Alan BorningAll 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
- A favorite introductory language in quite a few CS departments
Locally available versions
- MIT scheme: on NT machines and instructional unix servers
- DrScheme (from Rice University)
- See the 341 web page for directions on how to use
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 innermost-first
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 3.14) ? #(a 2 3.14)
Symbols evaluate byvariable lookup
Symbols:x ? 9mylist ? ("squid" "clam" "barnacle")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("squid" 2 3) ?Error: procedure application: expected procedure, given: "squid"; arguments were: 2 3
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 a symbols value
- Symbols evaluate by variable lookup:factorial ? #<procedure:factorial>
- How do we enter a symbol as a value?e.g., suppose we want:x ? octopusWhat do we write?(define x )
Suppressing evaluation
Error: reference to undefined identifier: octopus
- octopus is evaluatedSince symbols are evaluated by variable lookup we get the error
- But we want the symbol octopus
- Solution: must suppress the evaluationof the symbol octopus
quote special form
(define x (quote octopus))
Gets evaluated to initialize space x is bound to
(quote octopus) ? octopus
Because quote is a special form,octopus does not get evaluated when
evaluating the list (quote octopus).
quote just returns its argument un-evaluated!
Quoting
(quote octopus) ? octopus
- is shorthand because quote is so usefuloctopus ? octopus
- 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)
- Note: in MIT Scheme, eval takes a second argument (the environment). So instead write: (eval (+ 1 2 3) user-initial-environment)
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!
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
Thinking recursively
- Describe the answer
- do not worry about how to compute it directly
- decompose the problem into a simpler problem
- state the incremental step from the simpler problem to the general problem
- Consider the base case(s)
- get an intuition using the base cases
- test solution on base cases to ensure they give the right answers
Recursion is aboutbeing a smart-aleck!
- Whats the sum of the first 9 integers?
- 9 + sum of the first 8 integers!
- What is the length of a list?
- 1 + length of the rest of the list!
Recursion templates
- Augmenting recursion: build up the answer bit by bit
- Conditional augmentation (variant of augmenting recursion)
- Simultaneous recursion on several variables
Augmenting recursion examples
- already saw one: factorial ;; double each number in a list (map ;; defined later is a cleaner solution)(define (double-each s)
(if (null? s) () (cons (* 2 (car s)) (double-each (cdr s)))))
More augumenting recursion
;; simple version of built-in append function(define (my-append x y) (if (null? x) y (cons (car x) (my-append (cdr x) y))))
Reducing functions
- Yet another kind of augumenting recursion reduce a list of elements to a single element;; sum the elements of a list (define (sumlist x) (if (null? x) 0 (+ (car x) (sumlist (cdr x)))))
Tail recursive version offactorial
(define (fact-iter product counter max-count)
(if (> counter max-count)
[fact-iter (* counter product)
Tail recursive factorial trace
(factorial 4) ;; product counter max
counter > max, so terminate
Conditional augumentation
(define (positive-numbers x) (cond ((null? x) ()) ((> (car x) 0) (cons (car x) (positive-numbers (cdr x)))) (else (positive-numbers (cdr x))))) (filter will provide a cleaner solution)
Insertion sort
(define (insert x s) (cond ((null? s) (list x)) ((< x (car s)) (cons x s)) (else (cons (car s) (insert x (cdr s)))))) (define (isort s) (if (null? s) () (insert (car s) (isort (cdr s)))))
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?
(eq? a "foo") ? unspecified (likely #f)
- 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"
Side effects and Scwm
- Scwm, the Scheme Constraints Window Manager, relies on side-effects to do much of its work
- (iconify-window (get-window))
(list-all-windows [lambda (w)
(string=? (window-class w) "XTerm"))]))
for-each procedure
- Similar to map, but does not return list of returned values from the applications
- The applications are only used for their side effects; e.g.
- iconifying a window
- displaying a string
- Like begin, for-each is unnecessary if writing in a purely functional style
Commenting example
;;; Code by Greg J. Badros
"Describe procedure foo here."
;; this comment is indented but a full line
(for-each display args)) ; write out args
Final close parentheses donot go on separate lines
Commenting style
- ;;; comments flush-left on own line
- ;; comments indented on own line
- ; short comments after code segment
- Use editors mode-specificindentation facilities
- Use editor that matchesparentheses for you
Memory management
- Lots of new values getting constructed on the scheme heap
- Where do all the values go?
- (define x "Hello")
- (define x "World")
- What happens to the value "Hello"?
Scheme memory model
- Provides an abstract machinethat has infinite memory
- Implementation can choose to re-use real (physical) memory whenever it can without breaking the abstraction
- Process used: Garbage collectionRe-claims memory consumed by values that are no longer usable (i.e., that are garbage)
A view of garbage
(define x "Hello")
(define x "World")
Mark and sweepgarbage collection
Simple GC algorithm is mark and sweep
- for each top-level binding
- mark the values that thevariables are bound to; and
- recursively mark the memorythat those values use
- reclaim all not-marked memory
Reference counting
- Let each value keep track of how many other values or bindings point at it
- When reference count drops to zero,reclaim that values storage
Reference countingnever reclaims cycles
- Nothing is bound to the cyclic list,(a b c a b c a b c ), but countsare still > 0
Mark and sweep vs.Reference counting
- Mark and sweep can take a while
- Sometimes noticeable pauses in interaction
- Various improvements on the basic algorithm can help dramatically (including generation-based garbage collection, incremental garbage collection, etc)
- No extra overhead managing reference counts
- Reference counting
- Incremental, so no noticeable pauses, but extra overhead throughout computation
- Cyclic structures are not reclaimed