;; The first three lines of this file were inserted by DrRacket. They record metadata ;; about the language level of this file in a form that our tools can easily process. #reader(lib "htdp-beginner-reader.ss" "lang")((modname lect02c) (read-case-sensitive #t) (teachpacks ()) (htdp-settings #(#t constructor repeating-decimal #f #t none #f ()))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Union types: ;;; How to handle a data-type that can be of type A OR B? ;;; (that is: since types are sets-of-values: ;;; how to handle any value from the set (union A B)? ) ;;; Example: the rank of a playing-card. ;;; (We'll deal with the suit tomorrow.) ; A rank is either: ; - an integer ; or - a symbol ; Examples of the data, for rank: 5 'queen ;;; Writing code to handle a union type: ;; I want a function to help my bridge-playing friend: ;; you assign 'points' to each card; 2-10 are worth zero ;; but J,Q,K,A are worth 1,2,3,4 resp. ;; A stub function: ; rank->pts: rank -> integer #;(define (rank->pts r) 17) ;;; test cases (write 'em first!) (check-expect (rank->pts 5) 0) (check-expect (rank->pts 'queen) 2) ;;; Since we're handling data that can be integer OR character, ;;; we'll use "cond", which is like if-else-if: #;(define (rank->pts cr) (cond [(integer? cr) 0] [(symbol? cr) ...])) ;;; Depending on how paranoid we are, we can add one extra branch ;;; for protection: #;(define (rank->pts cr) (cond [(integer? cr) ...] [(symbol? cr) ...] [else ...])) ;;; Aside: the syntax of `cond`: #;(cond [q1 a1] [q2 a2] ... [qn an]) ;;; (Think q,a for question,answer.) ;;; Each of q1..qn is any expression (had better eval to a boolean), ;;; or it's the word `else` which is equivalent to `#t`. ;;; Each of a1...an are any expression. ;;; ;;; The semantics of cond: ;;; Evaluate q1; if it's true then return result of eval'ing a1; otherwise ;;; Evaluate q2; if it's true then return result of eval'ing q2; otherwise ;;; ... ;;; Evaluate qn; if it's true then return result of eval'ing qn; otherwise ;;; raise an error (in student-languages -- full lang returns (void)). ;;; q1...qn are either ;;; Okay, let's get back to writing rank->pts: #;(define (rank->pts cr) (cond [(integer? cr) ...] [(symbol? cr) ...])) ; We fill in each `...`. ; The first is pretty easy. ; The second will be ... a whole 'nother cond. ; rank->pts: rank -> integer ; (define (rank->pts cr) (cond [(integer? cr) 0] [(symbol? cr) (cond [(symbol=? cr 'jack) 1] [(symbol=? cr 'queen) 2] [(symbol=? cr 'king) 3] [(symbol=? cr 'ace) 4])])) ; A few questions you probably have: ; ; Could we collapse the nested conds? ; Yes, but I don't: ; The outer cond is there for a specific reason: ; it follows from the fact that *any* function handling a rank ; needs to ask those questions. ; The inner cond is doing work specific to this function. ; ; Could we have used an `if` instead of the outer `cond`? ; Yes, but again we use the `cond` as a signal to other programmers ; that we're doing a *case analysis* on the *type* of the input.