#lang plai-typed ;; The first line above is important. It tells DrRacket which ;; variant of Racket you want to use. We use a variant that ;; matches the textbook. Unlike most variants of Racket, ;; `plai-typed' is statically typed. ;; This is a comment that continues to the end of the line. ; One semi-colon is enough. ;; A common convention is to use two semi-colons for ;; multiple lines of comments, and a single semi-colon ;; when adding a comment on the same ;; line as code. #| This is a block comment, which starts with "#|" and ends with a "|#". Block comments can be nested, which is why I can name the start and end tokens in this comment. |# ;; #; comments out a single form. We use #; below ;; to comment out error examples. #;(/ (+ 1 1) 0) ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Built-in atomic data ;; Booleans true false #t ; another name for true, and the way it prints #f ; ditto for false ;; Numbers 1 0.5 1/2 ; this is a literal fraction, not a division operation 1+2i ; complex number ;; Strings "apple" "banana cream pie" ;; Symbols 'apple 'banana-cream-pie 'a->b '#%$^@*&?! ;; Characters (unlikely to be useful) #\a #\b #\A #\space ; same as #\ (with a space after \) ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Built-in functions on atomic data (not true) ; => #f (not false) ; => #t (+ 1 2) ; => 3 (- 1 2) ; => -1 (* 1 2) ; => 2 ;; etc. (< 1 2) ; => #t (> 1 2) ; => #f (= 1 2) ; => #f (<= 1 2) ; => #t (>= 1 2) ; => #f (string-append "a" "b") ; => "ab" (string=? "a" "b") ; => false (string-ref "a" 0) ; => #\a (char=? #\a #\b) ; => #f (equal? "apple" "apple") ; => #t (string=? "apple" "apple") ; => #t (equal? "apple" (string-append "a" "pple")) ; => #t (eq? 'apple 'apple) ; => #t (eq? 'apple 'orange) ; => #f (eq? "apple" "apple") ; => #t (eq? "apple" (string-append "a" "pple")) ; => #f ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Types 1 ; => 1, but says "number" first (+ 1 1/2) ; => 3/2, but says "number" first "a" ; => "a", but says "string" not ; => #, but says "(boolean -> boolean)" #;(+ 1 "1/2") ; error: number vs string ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; S-expressions '(+ 1 2) ; => '(+ 1 2), but says "s-expression" first '1 ; => 1, but says "s-expression" first '"a" ; => "a", but says "s-expression" first 'a ; => 'a, but says "symbol" first (?!) (symbol->s-exp 'a) ; => 'a, but says "s-expression" first (s-exp->number '1) ; => 1, but says "number" first (number->s-exp 1) ; => 1, but says "s-expression" first #;(s-exp->string '1) ; says "string", but then reports an error ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Basic expression forms ;; Procedure application ;; ( ...) (not true) ; => #f (+ 1 2) ; => 3 (string-append "a" "b") ; => "ab" ;; Conditionals ;; (cond ;; [ ] ...) ;; (cond ;; [ ] ... ;; [else ]) (cond ; [(< 2 1) 17] ; [(> 2 1) 18]) ; => 18 (cond ; second expression not evaluated [true 8] ; [false (/ 1 0)]) ; => 8 (cond ; in fact, second test not evaluated [true 9] ; [(zero? (/ 1 0)) 0]) ; => 9 (cond ; any number of cond-lines allowed [(< 3 1) 0] ; [(< 3 2) 1] ; [(< 3 3) 2] ; [(< 3 4) 3]) ; => 3 (cond ; else allowed as last case [(eq? 'a 'b) 0] ; [(eq? 'a 'c) 1] ; [else 2]) ; => 2 ;; If ;; (if ) (if (< 3 1) ; simpler form for single test "apple" ; "banana") ; => "banana" ;; And and Or ;; (and ...) ;; (or ...) (and true true) ; => #t (and true false) ; => #f (and (< 2 1) true) ; => #f (and (< 2 1) (zero? (/ 1 0))) ; => #f (second expression is not evaluated) (or false true) ; #t (or false false) ; #f (or (< 1 2) (zero? (/ 1 0))) ; => #t (second expression is not evaluated) (and true true true true) ; => #t (or false false false) ; => #f ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Built-in compound data ;; Lists empty ; => '() (list 1 2 3) ; => '(1 2 3), but shows "(listof number)" first (cons 0 (list 1 2 3)) ; => '(0 1 2 3) (list 'a 'b) ; => '(a b), because ' also distributes to elements #;(list 1 'a) ; error: number vs symbol (list "a" "b") ; => '("a" "b") (list (list 1 2) (list 3)) ; '((1 2) (3)) (cons 1 empty) ; => '(1) (cons 'a (cons 'b empty)) ; => '(a b) (list (list 1) empty) ; => '((1) ()) (cons (list 1) empty) ; => '((1)) (append (list 1 2) empty) ; => '(1 2) (append (list 1 2) (list 3 4)) ; => '(1 2 3 4) (first (list 1 2 3)) ; => 1 (rest (list 1 2 3)) ; => '(2 3) (first (rest (list 1 2))) ; => 2 (list-ref (list 1 2 3) 2) ; => 3 '(1 2 3) ; => '(1 2 3), but says "s-expression" first (first (s-exp->list '(1 2 3))) ; => 1, but says "s-expression" first ;; Vectors (vector 1 2 3 4) ; => '#(1 2 3 4), but shows "(vectorof number)" (vector-ref (vector 1 2) 0) ; => 1 ;; Boxes (box 1) ; => '#&1 (unbox (box 1)) ; => 1 ;; Tuples (values 1 2) ; => '#(1 2), but says "(number * number)" first (values 1 "a") ; => '#(1 "a"), but says "(number * string)" first ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Definitions ;; Defining constants ;; (define ) (define PI 3.14159) (* PI 10) ; => 31.4159 ;; Defining functions ;; (define ( ...) ) (define (circle-area r) (* PI (* r r))) (circle-area 10) ; => 314.159 (define (is-odd? x) (if (zero? x) false (is-even? (- x 1)))) (define (is-even? x) (if (zero? x) true (is-odd? (- x 1)))) (is-odd? 12) ; => #f ;; Definition matching a tuple (define-values (size color) (values 10 'red)) size ; => 10 color ; => 'red ;; Declaring types (define e : number 2.71828) ;; Declaring argument and result types ;; (define ( [ : ] ...) : ) (define (circle-perimeter [r : number]) : number (* 2 (* PI r))) (circle-perimeter 10) ; => 62.8318 ;; Defining datatypes ;; (define-type ;; [ ( : ) ...] ...) (define-type Animal [snake (name : symbol) (weight : number) (food : symbol)] [tiger (name : symbol) (stripe-count : number)]) (snake 'Slimey 10 'rats) ; => (snake 'Slimey 10 'rats) (tiger 'Tony 12) ; => (tiger 'Tony 12) (list (tiger 'Tony 12)) ; => (list (tiger 'Tony 12)) ; since use of `tiger' cannot be quoted #;(snake 10 'Slimey 5) ; error: symbol vs number (snake-name (snake 'Slimey 10 'rats)) ; => 'Slimey (tiger-name (tiger 'Tony 12)) ; => 'Tony (snake? (snake 'Slimey 10 'rats)) ; => #t (tiger? (snake 'Slimey 10 'rats)) ; => #f #; (snake? 5) ; error: number vs Animal ;; A type can have any number of variants: (define-type Shape [square (side : number)] [circle (radius : number)] [triangle (height : number) (width : number)]) (define (curvy? [s : Shape]) : boolean (circle? s)) (curvy? (square 5)) ; => #f (curvy? (circle 5)) ; => #t (curvy? (triangle 3 5)) ; => #f ;; Parameterized datatypes (like "generics" in Java) ;; (define-type ( ' ...) ;; [ ( : ) ...] ...) (define-type (Tree 'a) [leaf (val : 'a)] [node (left : (Tree 'a)) (right : (Tree 'a))]) (leaf 10) ; => (leaf 10), but says "(Tree number)" first (leaf (circle 8)) ; => (leaf 10), but says "(Tree Shape)" first ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Local binding forms ;; Local ;; (local [ ...] ) (local [(define x 10)] (+ x x)) ; => 20 #;x ; error: unbound identifier (define (to-the-fourth x) (local [(define (squared n) (* n n))] (* (squared x) (squared x)))) (to-the-fourth 10) ; => 10000 (local [(define (odd? x) (if (zero? x) false (even? (- x 1)))) (define (even? x) (if (zero? x) true (odd? (- x 1))))] (odd? 12)) ; => #f ;; Let (more traditional but less regular) ;; (let ([ ] ...) ) (let ([x 10] [y 11]) (+ x y)) ; => 21 (let ([x 0]) (let ([x 10] [y (+ x 1)]) (+ x y))) ; => 11 (let ([x 0]) (let* ([x 10] [y (+ x 1)]) (+ x y))) ; => 21 ;; Datatype case dispatch ;; (type-case ;; [ ( ...) ] ...) ;; (type-case ;; [ ( ...) ] ... ;; [else ]) (type-case Animal (snake 'Slimey 10 'rats) [snake (n w f) n] [tiger (n sc) n]) ; => 'Slimey (define (animal-name a) (type-case Animal a [snake (n w f) n] [tiger (n sc) n])) (animal-name (snake 'Slimey 10 'rats)) ; => 'Slimey (animal-name (tiger 'Tony 12)) ; => 'Tony #;(animal-name 10) ; error: number vs Animal #;(type-case Food ...) ; error: Food is not a defined type #;(define (animal-weight a) (type-case Animal a [snake (n w f) w])) ; error: missing tiger case (define (animal-weight a) (type-case Animal a [snake (n w f) w] [else 0])) ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; First-class functions ;; Anonymous function: ;; (lambda ( ...) ) (lambda (x) (+ x 1)) ; => #, but shows "(number -> number)" first ((lambda (x) (+ x 1)) 10) ; => 11 (define add-one (lambda (x) (+ x 1))) (add-one 10) ; => 11 (define add-two : (number -> number) (lambda (x) (+ x 2))) (add-two 10) ; => 12 (define (make-adder n) (lambda (m) (+ m n))) (make-adder 8) ; => # (define add-five (make-adder 5)) (add-five 12) ; => 17 ((make-adder 5) 12) ; => 17 (map (lambda (x) (* x x)) (list 1 2 3)) ; => (list 1 4 9) ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Side-effects ;; IMPORTANT: in this class using a side-effect is ;; usually wrong; avoid all side-effects ;; set! and begin ;; (set! ) ;; (begin *) (define count 0) (set! count (+ count 1)) ; => count ; => 1 (begin (set! count (+ count 1)) count) ; => 2 (local [(define x 0)] ; note: demonstrates set! in local, (begin ; but it's terrible style (set! x (list x)) ; (set! x (list x)) ; x)) ; => '((0)) ;; set-box! is a function: (define B (box 10)) (set-box! B 12) ; => B ; => (box 12) (unbox B) ; => 12 ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Polymorphic functions (define identity (lambda (x) x)) identity ; => #, but shows "('a -> 'a)" first (identity "a") ; => "a" (identity 1) ; => 1 empty ; => '(), but shows "(list 'a)" first cons ; => #, but shows "('a (listof 'a) -> 'a)" first (define b (box empty)) b ; => '#&(), but shows "(boxof (listof '_a))" first (set-box! b (list 1)) ; => #;(set-box! b (list "a")) ; error: number vs string ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Testing ;; test, test/pred, and test/exn functions: (test (+ 5 5) 10) ; => displays (good 10 10 "...") (test (+ 5 4) 10) ; => error-displays (bad 9 10 "...") (test/exn (cond) "no matching") ; => displays (good ...) (test/exn (cond) "bad dog") ; => error-displays (exception ...) (test/exn (cond [true 10]) "no matching") ; => error-displays (exception ...) (print-only-errors true) (test (+ 5 5) 10) ; => no display (test (+ 5 4) 10) ; => error-displays (bad 9 10 "...") ;; We'll often put tests in a `test` submodule, which is ;; run after the enclosing module --- even if the submodule ;; appears before a part of the enclosing module. Multiple ;; `(module+ test ...)` forms are effectively combined. (module+ test (test (negate 10) -1)) ; => error-displays (bad (negate 10) -10 -1) ;; The above test can call this function, even though ;; the function's definition appears later: (define (negate x) (- 0 x)) ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; More information ;; Use the "Help Desk" item in DrRacket's "Help" menu ;; See "PLAI Typed Language" ;; and maybe "Guide"