meeting -*- Outline -*- * an abstraction for inductive data types (2.2) This is very important for dealing with grammars, especially parse trees If you use Typedscm in DrScheme (or scheme342typed) you get these pieces of syntax automatically ** declaration and use (2.2.1) This is applying the automation principle to data structures Will be one of our major tools in study of interpreters ------------------------------------------ ABSTRACTION FOR INDUCTIVE DATA (2.2) problem: want representation of grammars ::= | ( ) Want: constructors access without all the tedium Solution: defining constructors: access syntax: ------------------------------------------ *** define-datatype ------------------------------------------ DEFINE-DATATYPE DESCRIBES CONSTRUCTORS For the grammar: ::= | ( ) (define-datatype This defines the following procedures leaf-node : interior-node : bintree? : ------------------------------------------ ... bintree bintree? ; type name, type predicate name (leaf-node ; variant name (datum number?)) ; field and type predicate for its type (interior-node ; another variant (key symbol?) (left bintree?) (right bintree?))) explain the notation, don't need to write out the comments above. The idea is that a bintree is either a leaf-node record, or an interior node record, hence it's a variant record type. The reason for the type predicate is to allow run-time type checks on arguments passed to the fields. ... leaf-node : (-> (number) bintree) interior-node : (-> (symbol bintree bintree) bintree) bintree? : (type-predicate-for bintree) A type-predicate-for type is a subtype of (-> (datum) boolean) that records the additional fact that bintree? returns true when it's argument is a boolean. This is used by the type checker to help it understand what the type of the "left" and "right" fields are. ------------------------------------------ SYNTAX ::= ( define-datatype { ( {}* ) }+ ) ::= ( ) ::= ::= ::= ::= ------------------------------------------ Note that there has to be at least one variant The syntax requires the type predicate name due to the R5RS macros, which don't allow names to be computed at macro expansion time ------------------------------------------ SEMANTICS (define-datatype tn tn? (vn1 (fn11 pe11) ... (fn1q pe1q)) ... (vnk (fnk1 pek1) ... (fnkr pekr))) where pe11 : (type-predicate-for te11) ... pe1q : (type-predicate-for te1q) ... pek1 : (type-predicate-for tek1) ... pekr : (type-predicate-for tekr) declares the constructors vn1 : (-> (te11 ... te1q) tn) ... vnk : (-> (tek1 ... tekr) tn) and the type predicate tn? : (type-predicate-for tn) and the type name tn : (variant-record (vn1 (fn11 te11) ... (fn1q te1q)) ... (vnk (fnk1 tek1) ... (fnkr tekr))) ------------------------------------------ may want to go back to the example now A variant-record type is just the type system's way of recording the information in the declaration for later use, not that important here. Note that order of the variants doesn't matter, but the order of the fields within variants does. However, the field names *don't* matter, only the number and types of the fields matter. ------------------------------------------ FOR YOU TO DO What are the types of the constructors for (define-datatype vehicle vehicle? (bicycle (maker symbol?)) (auto (maker symbol?) (year number?))) How would you make an object that describes: 1. A schwinn bicycle? 2. A 1991 Honda auto? ------------------------------------------ *** variant records ------------------------------------------ VARIANT RECORDS def: a *union type* is a type that contains values of ______________ different types. def: a *discriminated union type* is a union type whose values def: a *variant record type* is a type that is a discriminated union type, in which the contained types are all _________________. ------------------------------------------ ... two or more e.g., number, datum are both union types ... are tagged, so that the type a value belongs to can be discriminated ... record types. Q: When is a type defined with define-datatype a record type? Q: When is it a variant-record type? Q: What's a variant-record type like in Java or C++? a type hierarchy Common source of insecurity: using variant records as if they were objects of one type, when really another But in Ada have discriminated unions: can always tell what part a value is from (even if the types are the same, as in enumeration) Variant record types are convenient for working with grammars, where you need to know what kind of tree you have and each tree has several subtrees of different types *** cases expressions ------------------------------------------ CASES EXPRESSION ALLOWS ACCESS (define small-tree (interior-node 'a (leaf-node 7) (leaf-node 3))) (leaf-sum (leaf-node 7)) ==> 7 (leaf-sum small-tree) ==> 10 (leaf-sum (interior-node 'b small-tree small-tree)) ==> 20 (deftype leaf-sum (-> (bintree) number)) (define leaf-sum (lambda (tree) (cases ------------------------------------------ ... bintree tree ; note the type name! (leaf-node (datum) datum) (interior-node (key left right) ; can't omit any fields (+ (leaf-sum left) (leaf-sum right))) ))) Note how this follows the grammar (i.e., the shape of the data), in terms of its recursion pattern This is like cases expression in ML Q: What's this like in C? In Pascal? ------------------------------------------ SYNTAX ::= ( cases {}+ ) | ( cases {}* ( else ) ) ::= ::= ( ( {}* ) ) ::= ::= ::= ------------------------------------------ the real syntax allows ::= {}* You have to give the type name each time, the else clause is optional only if you list all the variants. (We skipped the semantic details, as they were clear already) ------------------------------------------ SEMANTICS (cases tn e (vn1 (fn11 ... fn1q) e1) ... (vnk (fnk1 ... fnkr) ek) (else ee)) check that: e : tn tn : (variant-record (vn1 (fn11 te11) ... (fn1q te1q)) ... (vnk (fnk1 tek1) ... (fnkr tekr)) ...) there is some T such that, ee : T and for all 1 <= i <= k, ei : T If for some 1 <= i <= k, e = (vni v1 ... vs) then return otherwise return ee. ------------------------------------------ ... (let ((fni1 v1) ... (fnis vs)) ei) The else clause can be omitted only if the checking is exhaustive. Omitting the else thus doesn't affect the type (not assumed to be void) ------------------------------------------ FOR YOU TO DO Write the procedure height: (-> (bintree) number) Examples: (define small-tree (interior-node 'a (leaf-node 7) (leaf-node 3))) (height (leaf-node 7)) ==> 1 (height small-tree) ==> 2 (height (interior-node 'b small-tree small-tree)) ==> 3 ------------------------------------------