λ => Scheme for Rubyists
Scheme History Authors: Guy Steele and Gerald Sussman Structure and Interpretation of Computer Programs (SICP) by Abelson & Sussman
Scheme in Real Life GIMP (Script-Fu) Guile (official extension language of GNU) Lily Pond (sheet music engraving)
Scheme is a Lisp dialect LISP ==> “LIS t P rocessing language ”
Scheme is a Lisp dialect LISP ==> “LIS t P rocessing language ” LISP ==> L ots of I rritating S uperfluous P arentheses
Lisp Family of languages: Common Lisp Scheme Emacs Lisp Clojure Arc First incarnation by John McCarthy, 1958 older than COBOL (!)
Lisp vs. the World Prefix notation (+ x y) (< x y) (eq? x y)
Lisp vs. the World No operator precedence rules (mandatory parentheses eliminate the need) (sqrt (+ (/ (* 3 4) 2) 3))
Lisp vs. the World No operator precedence rules (mandatory parentheses eliminate the need) (sqrt (+ (/ (* 3 4) 2) 3)) ==> 3
Lisp vs. the World Functions are first-class objects, so we can: Assign them to variables Pass them as parameters to other functions Return them from other functions
Lisp vs. the World Example of 1st-class function use in Scheme (Scheme's map =~ Ruby's map or collect ) (define square (lambda (x) (* x x))) (map square (list ))
Lisp vs. the World Example of 1st-class function use in Scheme (Scheme's map =~ Ruby's map or collect ) (define square (lambda (x) (* x x))) (map square (list )) ==> ( )
Lisp vs. the World Linked lists are built in; based on pairs Scheme representation: (1 2 “a” 4 #\b) 1 2 “a” 4 #\b (empty list)
Scheme vs. Other Lisps Optimized tail recursion “Normal” looping constructs are not used Tail-recursive procedures are replaced by iterative version in compilation Why? Even for mutually recursive procedures
Scheme vs. Other Lisps Optimized tail recursion “Normal” looping constructs are not used Tail-recursive procedures are replaced by iterative version in compilation Why? So we don't blow the stack Even for mutually recursive procedures
Mutual Tail Recursion (define even? (lambda (x) (if (= x 0) #t (odd? (- x 1))))) (define odd? (lambda (x) (if (= x 0) #f (even? (- x 1)))))
Why use recursion? Some problems are more cleanly solved
Why use recursion? Some problems are more cleanly solved Especially in mathematics factorial(n) = { 1 if n==0 or n==1 n * factorial(n-1)
Why use recursion? (define factorial (lambda (n) (cond ((or (= n 1) (= n 0)) 1) (else (* n (factorial (- n 1))))))) Nearly identical to math definition Any problems?
Why use recursion? (define factorial (lambda (n) (cond ((or (= n 1) (= n 0)) 1) (else (* n (factorial (- n 1))))))) Nearly identical to math definition Any problems? not tail-recursive
Why use recursion? (define factorial (lambda (n) (define factorial2 (lambda (n acc) (cond ((or (= n 1) (= n 0)) acc) (else (factorial2 (- n 1) (* n acc)))))) (factorial2 n 1))) Now we're tail-recursive Is this better?
Why use recursion? (define factorial (lambda (n) (define factorial2 (lambda (n acc) (cond ((or (= n 1) (= n 0)) acc) (else (factorial2 (- n 1) (* n acc)))))) (factorial2 n 1))) Now we're tail-recursive Is this better? it depends
A Tale of Two Factorial Solutions (factorial 5) (* 5 (factorial 4)) (* 5 (* 4 (factorial 3))) (* 5 (* 4 (* 3 (factorial 2)))) (* 5 (* 4 (* 3 (* 2 (factorial 1))))) (* 5 (* 4 (* 3 (* 2 1)))) (* 5 (* 4 (* 3 2))) (* 5 (* 4 6)) (* 5 (24)) 120 (factorial 5) (factorial2 5 1) (factorial2 4 5) (factorial2 3 20) (factorial2 2 60) (factorial ) 120 Non-tail-recursiveTail-recursive
Scheme vs. Other Lisps Scheme has a minimal set of standard features Common Lisp has lots of built-in libraries One namespace :( Continuations Representation of the state of execution (think goto on steroids) Also available in Ruby ( callcc )
Prefix Notation (+ 2 7) => 9 (* 2/3 5/8) => 5/12 (/ 1.0 3) => (= 2 (+ 0 3)) => #f (not (= (> 2 (+ 6 1)) #t)) => #t
Defining Procedures (define add (lambda (x y) (+ x y))) (define truthy? (lambda (x) (not (= #f x)))) (define (truthy? x) (not (= #f x)))
Quoting (1 2 3) ;The object 1 is not applicable. (quote (1 2 3)) ==> (1 2 3) '(1 2 3) ==> (1 2 3) 'testing ==> testing
Applying Procedures (add 7 17) => 24 (truthy? '(1 2 4)) => #t (truthy? #f) => #f (truthy? 78) => #t
Parsing Lists (car '(1 2 3)) ==> 1 (cdr '(1 2 3)) ==> (2 3)
Building Lists (cons 1 2) ==> (1. 2) (cons 1 (cons 2 '())) ==> (1 2) (empty list)
Building Lists Will this work the way we expect? (cons '(1 2 3) '(4 5 6))
Building Lists Will this work the way we expect? (cons '(1 2 3) '(4 5 6)) ==> ((1 2 3) 4 5 6)
Building Lists What about this? (cons 1 (cons 2 (cons 3 '(4 5 6))))
Building Lists What about this? (cons 1 (cons 2 (cons 3 '(4 5 6)))) ==> ( ) Yes, but this way sucks
Building Lists The simpler solution: (append '(1 2 3) '(4 5 6)) ==> ( )
Functional Programming The result depends only on the input(s) No side effects Easy to run in parallel
Scheme's functional-ness Scheme encourages functional programming But it doesn't require it (not purely functional) (define x '(1 2 3)) x ==> (1 2 3) (set-car! x 'stupid-symbol) x ==> (stupid-symbol 2 3) (set-cdr! x '(#t #f)) x ==> (stupid-symbol #t #f)
Getting Started with Scheme Download an implementation MIT-Scheme: Set up your text editor Emacs (indentation help) Textmate Bundle
Anything else we might want?
Test Manager: xUnit for Scheme Author: Alexey Radul Home Page: Documentation:
Test Manager Example (load “test-manager/load.scm”) (define-each-test (assert-equal 2 (factorial 2)) (assert-equal 6 (factorial 3)) (assert-equal 24 (factorial 4))) (run-registered-tests)
λ => Happy Happy Joy Joy