;; Agenda for today: ;; - new Scheme form: `let' ;; - from `cond' to `cases' ;; - concrete syntax, reprsentations, and abstract syntax ;; ---------------------------------------- ;; `let' ;; ---------------------------------------- ;; ::= ... ;; ::= (let (( )*) ) ;; Example: (let ((x (+ 2 3)) (y 10)) (+ x y)) ;; New evaluation rule: ;; ... (let ((1 1) ... (n n)) a) ... ;; ;; -> ;; ;; ... b ... ;; ;; where b is a with each i replaced by i ;; Example use: (let ((x (+ 2 3)) (y 10)) (+ x y)) ; -> (let ((x 5) (y 10)) (+ x y)) ; -> (+ 5 10) ;; Example use in a realistic program: ;; sour : -> ;; Turns all instances of 'milk in the list to 'yogurt ;; (sour '()) ->-> '() ;; (sour '(milk bread)) -> '(yogurt bread) (define (sour l) (cond [(null? l) '()] [else (let ([others (sour (cdr l))]) (cond [(eq? (car l) 'milk) (cons 'yogurt others)] [else (cons (car l) others)]))])) ;(sour '(milk bread)) ;-> ;(cond ; [(null? '(milk bread)) '()] ; [else (let ([others (sour (cdr '(milk bread)))]) ; (cond ; [(eq? (car '(milk bread)) 'milk) (cons 'yogurt others)] ; [else (cons (car '(milk bread)) others)]))]) ;-> ;(cond ; [#f '()] ; [else (let ([others (sour (cdr '(milk bread)))]) ; (cond ; [(eq? (car '(milk bread)) 'milk) (cons 'yogurt others)] ; [else (cons (car '(milk bread)) others)]))]) ;-> ;(cond ; [else (let ([others (sour (cdr '(milk bread)))]) ; (cond ; [(eq? (car '(milk bread)) 'milk) (cons 'yogurt others)] ; [else (cons (car '(milk bread)) others)]))]) ;-> ;(let ([others (sour (cdr '(milk bread)))]) ; (cond ; [(eq? (car '(milk bread)) 'milk) (cons 'yogurt others)] ; [else (cons (car '(milk bread)) others)])) ;->-> ;(let ([others '(bread)]) ; (cond ; [(eq? (car '(milk bread)) 'milk) (cons 'yogurt others)] ; [else (cons (car '(milk bread)) others)])) ;-> This is the key step! ;(cond ; [(eq? (car '(milk bread)) 'milk) (cons 'yogurt '(bread))] ; [else (cons (car '(milk bread)) '(bread))]) ;-> ;... ;; ---------------------------------------- ;; from `cond' to `cases' ;; ---------------------------------------- ;; Suppose we want to track information about a zoo ;; that has lions, tigers, and bears. ;; Relevant info: - name and weight of every animal ;; - stripe count for tigers ;; - for a bear, whether it's hibernating ;; ::= (list 'lion ) ;; ::= (list 'tiger ) ;; ::= (list 'bear ) ;; Lion Fred is 250lbs (list 'lion 'fred 250) ;; Tiger Sue is 175lbs with 13 stripes (list 'tiger 'sue 175 13) ;; Bear Ellen is 50lbs not hibernating (list 'bear 'ellen 50 #f) ;; feed : -> ;; Feeds the given animal 5 lbs of food, and ;; returns the fatter animal ;; (feed '(lion fred 250)) ->-> '(lion fred 255) ;; (feed '(tiger sue 175 13)) ->-> '(tiger sue 180 13) ;; (feed '(bear ellen 50 #f)) ->-> '(bear ellen 55 #f) (define (feed a) (cond [(eq? 'lion (car a)) (let ([name (cadr a)] [weight (caddr a)]) (list 'lion name (+ 5 weight)))] [(eq? 'tiger (car a)) (let ([name (cadr a)] [weight (caddr a)] [sc (cadddr a)]) (list 'tiger name (+ 5 weight) sc))] [(eq? 'bear (car a)) (let ([name (cadr a)] [weight (caddr a)] [h? (cadddr a)]) (list 'bear name (+ 5 weight) h?))])) (feed '(lion fred 250)) '->-> '(lion fred 255) (feed '(tiger sue 175 13)) '->-> '(tiger sue 180 13) (feed '(bear ellen 50 #f)) '->-> '(bear ellen 55 #f) ;; The above works, but many parts were painful. Also, ;; there's lots of room for mistakes with little ;; error checking. ;; We can start to address the error-checking problem by defining ;; animal constrcutors (i.e., animal-making functions): (define (lion name weight) (if (not (symbol? name)) (eopl:error 'lion "not a name symbol")) (if (not (number? weight)) (eopl:error 'lion "not a weight number")) (list 'lion name weight)) (define (tiger name weight sc) (if (not (symbol? name)) (eopl:error 'tiger "not a name symbol")) (if (not (number? weight)) (eopl:error 'tiger "not a weight number")) (if (not (number? sc)) (eopl:error 'tiger "not a stripe-count number")) (list 'tiger name weight sc)) (define (bear name weight h?) (if (not (symbol? name)) (eopl:error 'bear "not a name symbol")) (if (not (number? weight)) (eopl:error 'bear "not a weight number")) (if (not (boolean? h?)) (eopl:error 'bear "not a hibernating boolean")) (list 'bear name weight h?)) ;; Using the above functions, we get much better error reporting, ;; and our `feed' function is somewhat easier to read: (define (feed a) (cond [(eq? 'lion (car a)) (let ([name (cadr a)] [weight (caddr a)]) (lion name (+ 5 weight)))] [(eq? 'tiger (car a)) (let ([name (cadr a)] [weight (caddr a)] [sc (cadddr a)]) (tiger name (+ 5 weight) sc))] [(eq? 'bear (car a)) (let ([name (cadr a)] [weight (caddr a)] [h? (cadddr a)]) (bear name (+ 5 weight) h?))])) ;; Still, there's an awful lot of `cadddr' noise, and writing ;; the error-checkign functions was a lot of work. ;; Instead of writing the error-checking functions by hand, ;; we can use `define-datatype'. Then, we can use `cases' to ;; more easily implement a function on animals. ;;; define-datatype (define-datatype animal ; the datatype name animal? ; the name of a predicate to define to recognize all animal values (lion ; the name of the first variant, to be defined as a functon to make lions [name symbol?] ; `name' is the name of the first field for error-reporting, ; and a legal value for the field must generate #t when ; passed to `symbol?' [weight number?]) ; `weight is the name of the second field, and a legal ; weight vakle must be a number (tiger ; the name of the second variant ... [name symbol?] [weight number?] [stripe-count number?]) (bear [name symbol?] [weight number?] [hibernating? boolean?])) (define (feed a) (cases animal ; the datatype name a ; an expression whose value is an animal [lion (name weight) ; name stands for the value of the first fiel, and ; weight stands for the values of the second field (lion name (+ 5 weight))] [tiger (name weight sc) (tiger name (+ 5 weight) sc)] [bear (name weight h?) (bear name (+ 5 weight) h?)])) (feed (lion 'fred 250)) '->-> (lion 'fred 255) (feed (tiger 'sue 175 13)) '->-> (tiger 'sue 180 13) (feed (bear 'ellen 50 #f)) '->-> (bear 'ellen 55 #f) #| Interaction example after executuing the file up to this point: Welcome to DrScheme, version 202. Language: Essentials of Programming Languages (2nd ed.) custom. 15 15 15 15 (list 'lion 'fred 250) (list 'tiger 'sue 175 13) (list 'bear 'ellen 50 false) (list 'lion 'fred 255) '->-> (list 'lion 'fred 255) (list 'tiger 'sue 180 13) '->-> (list 'tiger 'sue 180 13) (list 'bear 'ellen 55 false) '->-> (list 'bear 'ellen 55 false) (make-lion 'fred 255) '->-> (make-lion 'fred 255) (make-tiger 'sue 180 13) '->-> (make-tiger 'sue 180 13) (make-bear 'ellen 55 false) '->-> (make-bear 'ellen 55 false) > (cases animal (tiger 'Joe 300 17) [lion (n w) 10] [tiger (n w sc) 13]) cases: missing cases for the following variants: bear in: (cases animal (tiger (quote joe) 300 17) (lion (n w) 10) (tiger (n w sc) 13)) > (cases animal (tiger 'Joe 300 17) [lion (n w) 10] [tiger (n w sc) 13] [brea (n w h?) 15]) cases: not a variant of `animal' in: brea > (cases animal (tiger 'Joe 300 17) [lion (n w) 10] [tiger (n w sc) 13] [bear (n w h?) 15]) 13 > (cases animal (tiger 'Joe 300 17) [lion (n w) 10] [tiger (n w sc) n] [bear (n w h?) 15]) 'joe > (cases animal (tiger 'Joe 300 17) [lion (n w) 10] [tiger (n w sc) sc] [bear (n w h?) 15]) 17 > |# ;; ---------------------------------------- ;; concrete syntax, reprsentations, and abstract syntax ;; ---------------------------------------- #| Parsing ------- When we write ::= '() ::= (cons ) we are defining a set of test strings. The following text strings are in the : '() (cons 'a '()) Up to now, we've been careful to define sets of text strings such that the members are also in the set of legal Scheme text strings. Furthermore, we've used the Scheme values produced by those text strings to represent members of the defined set. For example, we've used Scheme's empty list to repesent the '() member of . But now we've started presenting animals in a different way. In particular, even though we defined as ;; ::= (list 'lion ) ;; ::= (list 'tiger ) ;; ::= (list 'bear ) so that (list 'lion 'fred 200) is in a member of , the value produced when using that member as a Scheme expression is not an instance of the `animal' datatype: > (animal? (list 'lion 'fred 200)) #f > (animal? (lion 'fred 200) #t This is the difference between the _concrete syntax_ defined via BNF, and the _reprsentation_ of members of the syntax. In general, we have to _parse_ an bit of concrete syntax to get its presentation: Concrete Syntax (set defns) ---- parser ----> Representation Note that our latest the representation of a lion doesn't need to account for the fact that the concrete syntax uses `list' or the symbol 'lion. It only encodes the important part of the concrete syntax. The "important part" corresponds to the _abstract syntax_, and it's really the abstract syntax that we repsent. We'll talk more about parsers next week... Choosing Representations ------------------------ For any particular set, there is always a choice of reprsentations. For the most part, we'll use `define-datatype'-based reprsentations from now on. In the case of lists (such as lists of symbols), we'll tend to do things as before, though. In other words, given ;; ::= '() ;; ::= (cons ) we'll tend to use the Scheme empty list to represent '(), etc. Note that we *could* use `define-datatype' to generate a datatype for animal lists, though. It would look like this: (define-datatype list-of-animal list-of-animal? (empty-zoo) (cons-zoo [a animal?] [l list-of-animal?])) Printing -------- Just like work is required in general to go from concrete syntax to its reprsentation, work is required to go from the reprsentation of a result to its printed form. When we changed DrScheme's output style to "Constructor", we made DrScheme print each value using a particular text string --- one that corresopnds to a Scheme expression that pruces the value again. That choice was particularly nice when concrete syntax also corresponded to Scheme expression. As it turns out, the traditional Scheme printer doesn't do that. Instead, it tries to drop a leading quote when printing a value. Switch drScheme's output style to `write' (using the language-selection dialog). Now, > 4 4 but > 'a a The quote gets dropped from the start of a symbol. More examples: > '(a (c d)) (a (c d)) > (list 'a (list 'c 'd)) (a (c d)) > (cons 'a '()) (a) > (cons 'a 'b) (a . b) > '(a . b) (a . b) > (a . b) application: bad syntax (illegal use of `.') in: (a . b) > '(1 2 3 . 4) (1 2 3 . 4) > (1 . 2 . 3 . 4) read: illegal use of "." > (1 . 2 . 3) . procedure application: expected procedure, given: 2; arguments were: 1 3 > '(1 . 2 . 3 . 4) read: illegal use of "." > '(1 . 2 . 3) (2 1 3) ;; two dots triggers an infix convention that you don't ;; want to worry about! > '((1 2) . 4) ((1 2) . 4) > (cons (list 1 2) 4) ((1 2) . 4) > '(4 . (1 2)) (4 1 2) > (cons 4 (list 1 2)) (4 1 2) > (lion 'fred 200) #(struct:lion fred 200) From now on, we'll use this traditional output style (which matches the book). |#