CS 541 Lecture -*- Outline -*- * Fundamentals of Denotational Semantics advert: the math that we use for denotations ADT view of objects manipulated in semantics use sets as an example awareness: use examples... ** abstract vs. concrete syntax (see Watt, section 2.4) concrete syntax is used to parse expressions abstract syntax used to generate trees may not work for parsing ** notation for defining functions (see Watt, section 3.1.2) essentially SML notation, except: don't use end in let expressions don't use val for value defs or fun for function definitions cases of function defs aren't separated by | use \ instead of fn for anonymous functions ** domains (see Watt, section 3.1.3) a domain is, for now, just a set with some operations defined on it (like an ADT) this is a simplification, but okay at the moment (the truth: domains are partially ordered sets) *** primitive domains **** Watt's assumed domains see also appendix D.1 ------------------------ PRIMITIVE DOMAINS Unit { () } Truth-Value { true, false } Character { `a', `b', ... } Integer { 0, 1, -1, ... } Natural { 0, 1, 2, ... } ENUMERATIONS Denomination = {dollars, cents} Error = { bus, segmentation } ------------------------ Q: can you think of anything that is the least bit vague about these? exactly how many characters are there? what are the primitive operations on these domains? **** Ways to present primitive domains ***** equational style (compare Watt chapter 6) the following is in an extension of the Larch Shared Language (compare Schmidt's example 3.2 on p. 36, and Watt's page 148) --------------------- EQUATIONAL SPECIFICATION OF DOMAINS Truth-Value: trait introduces \forall D if _ then _ else _ : Bool D D -> D true: Bool false: Bool not: Bool -> Bool both: Bool, Bool -> Bool either: Bool, Bool -> Bool asserts \forall b,b1:Bool, d1,d2:D (if true then d1 else d2) == d1 (if false then d1 else d2) == d2 not(true) == false not(false) == true both(true,b) == b both(false,b) == false either(true,b) == true either(false,b) == b (true = false) == false --------------------- the last inequation is a bit delicate, because need to exclude models in which true = false See Guttag and Horning's paper on Sufficient completeness for details. --------------------------- ANOTHER EQUATIONAL EXAMPLE Environ(Indentifier, Bindable): trait introduces empty-environ: Environ bind: Identifier, Bindable -> Environ overlay: Environ, Environ -> Environ find: Environ, Identifier -> Bindable asserts \forall env: Environ, I,I': Indentifier, b: Bindable find(bind(I,b),I) == b find(overlay(bind(I,b), env), I') == if Ids.equal(I,I') then b else find(env,I') implies converts find, exempting \forall I: Identifier find(empty_environ,I) --------------------------- Q: does this resemble any kind of programming you're familiar with? ***** program style (e.g., Schmidt's example 2.1 (p. 30)) This is often better for handling errors idea: program over the representation -either give the set which is the rep domain, or assume already know what rep domain means, -can use lambda calculus, extended with rules for conditionals and operations of representing domain note for below: fail = \bot = bottom, _ is subscript --------------------- PROGRAM STYLE SPEC OF DOMAINS Domain Rat = (Z x Z)_{\bot} Operations makerat: Z -> (Z -> Rat) makerat = \p.\q.if q=0 then fail else (p,q) addrat: Rat -> Rat -> Rat addrat = \(p1,q1).\(p2,q2). ((p1*q2)+(p2*q1),q1*q2) mulrat: Rat -> Rat -> Rat mulrat = \(p1,q1).\(p2,q2). (p1*p2,q1*q2) --------------------- the domain (Z x Z)_{\bot} will be explained shortly this can be translated into SML (approximately) as follows --------------------- TRANSLATION INTO SML signature RatSig = sig type Rat exception fail val makerat: int -> int -> Rat val addrat: Rat -> Rat -> Rat val mulrat: Rat -> Rat -> Rat end; structure Rat: RatSig = struct type Rat = int * int exception fail val makerat = fn p => fn q => if q = 0 then raise fail else (p,q) val addrat = fn (p1,q1) => fn (p2,q2) => ((p1*q2)+(p2*q1),q1*q2) val mulrat = fn (p1,q1) => fn (p2,q2) => (p1*p2,q1*q2) end; -------------------- *** cartesian product domains A x B (Watt, section 3.1.3) -------------- CARTESIAN PRODUCT DOMAIN A x B A x B = { (a,b) | a in A and b in B } As in SML, use (,) to construct use pattern matching to observe extensionality: (x,y) = (x',y') iff x = x' and y = y' GENERALIZED CARTESIAN PRODUCT A x ... x D = { (a, ... ,d) | a in A, ..., and d in D } CONSTRUCTOR and PATTERN MATCHING EXAMPLE let pay = (100, dollars) in ... let (amount, denom) = pay in ... --here amount = 100, denom = dollars -------------- note the absence of the val again **** disjoint union A + B (Watt, section 3.1.3) also known as sum or coproduct -------------- DISJOINT UNION DOMAIN A + B = { left a | a in A } union { right b | b in B } PREFERED NOTATION left A + right B = { left a | a in A } union { right b | b in B } Like SML's datatype D = left of A | right of B; As in SML, use the following to construct left: A -> (left A + right B) right: B -> (left A + right B) use pattern matching to observe extensionality: s1 = s2 iff both have same tag and value -------------- define what a tag is Q: why the disjointness? Q: do the tags always have to be left and right? no Q: can you generalize to finite disjoint unions? Q: define a disjoint union of 3 units, tagged yes, no and maybe? example: booleans, finite lists (Schmidt, page 43) **** function A -> B ------------------ (COMPUTABLE) FUNCTION DOMAINS A -> B = { \x.E | a in A implies [a/x]E in B } As in SML, use function def. syntax to construct -------------------- but doesn't use "fun" keyword or lambda (\) -------------------- use application syntax to observe extensionality: f = g iff for all x in A, f(x) = g(x) -------------------- emphasis on computable functions, because computer's don't deal with non-computable ones. so only computable functions (\-exps) are elements of domain Warning about recursion (Schmidt's section 3.3) arbitrary recursion is not allowed, so that mathematically definitions are well-defined e.g., q(x) = if (x = 0) then one else q(x plus one) this definition satisfied by many mathematical functions such as what? However, recursion over abstract syntax is always well-defined, and so is permitted (and fundamental to the approach). **** sequence A* ----------------------- SEQUENCE DOMAINS A* = nil + cons(A x A*) Watt writes cons as infix * a * s = cons(a,s) nil: A* *: (A x A*) -> A* ----------------------- **** lifted A_{\bot} = A union {\bot} ----------------------- LIFTED DOMAINS A = A union { | } | fail: A | fail = | Watt assumes *all* domains are lifted, and thus suppresses this notation. That is, all domains contain fail. Partial functions on A -> B are modeled by total functions on A -> B | i.e., (A -> B) = A -> B | | def: f: A -> B is strict iff | | f( | ) = | Watt uses only strict functions. ----------------------- In the above \bot (bottom) is written as |, the bottom of the | has to be crossed (it's an upside down T) In the rest of the notes, use \bot for bottom Bottom usually means "no information", "error", or nontermination it's all errors rolled into one. Using only strict functions simplifies the handling of errors, but restricts what can be modeled (e.g., streams) Alternative is to assume that \ makes non-strict functions (which it does, technically) and to have a separate notation for strict functions Schmidt's convention for this is strict lambda (\_) f: A_{\bot} -> B_{\bot} = \_x . M defined as f(\bot) = \bot f(a) = [a/x]M, if a is proper e.g, strict vs. nonstrict constant function let x=e1 in e2 means (\_x.e2)e1