CS 541 Lecture -*- Outline -*- * Introduction to Denotational Semantics Q: In groups of 2, can you come up with either: 1. ideas for giving a more "mathematical" semantics for programming languages 2. questions about what that would involve or what properties a more mathematical semantics would have ? advert: semantic technique that assigns to each phrase (piece of syntax) a meaning meaning of bigger phrases composed of meanings of subphrases. "compositional" so structure of semantics matches structure of language; helps make language orthogonal, regular, clean mathematically precise, doesn't rely on translation explains meaning of program without "execution" based on simple mathematical ideas: function, mappings views language as a method for expressing mathematical objects like functional programming, thus has the advantage/disadvantage that every changing aspect of the language's semantics can be seen as an argument and result. good for comparing languages ** Motivation: problems with operational semantics? (Read Tennant and Schmidt's introductions) (recall: operational good for describing implementations) how do you present semantics of underlying machine? translation may lose structure. input and output functions may do a lot of compilation/encoding (indeed all in the evaluation style) too much detail too hard to reason about programs! to understand what happens, user has to translate too, and then simulate the computation e.g. reader has to do a looping process to find the meaning of a loop meaning may be spread out among parts of semantics instead of reasoning about computations, want to reason about meanings directly want to deal with complex languages where computations are very complex in a simpler way ** Idea: meaning of program is a function (or relation) from inputs to outputs (more general: meaning is a mathematical object) *** claims -can lead directly to prototype implementations -precise (problem: is it flexible enough to leave impl decisions open?) *** vs. operational -abstract from *computation* performed by machine to its *behavior* (I/O relation) -directly describe \P.M[[P]], which is M itself. -structure of description of M matches language better than the machine model (which is simpler than language) -can look at denotation description for semantics of a particular feature, not all tied together -but not as easy to use for concurrency semantics *** vs. axiomatic (and algebraic) -directly describes function computed by program, instead of constraining it -not limited by proof techniques in what can be said about program -but not as easy to use for reasoning about programs ** Parts of denotational description compare to parts of a compiler *** Abstract syntax (of programs) describes structure of parse trees - Read: Schmidt ch. 1, or Watt section 2.4 don't worry about ambiguities, syntactic sugars *** Semantic algebras (domains) describes the values over which one computes e.g., natural numbers, locations, functions, ... defines set + operations (ADT) *** Valuation functions give denotations of each syntax tree in terms of objects in the semantic algebras *** Example (Binary numbers) Watt page 50 points: meaning of a numeral (syntax) is a number (semantics) note how structure of definition resembles grammar parts of a definition (including types) this is a simple example, real value comes when have looping, etc. --------------------------- DENOTATIONAL SEMANTICS EXAMPLE Abstract syntax: Numeral ::= Digit | Numeral Digit Digit ::= 0 | 1 Semantic Domains: I. Natural numbers Domain Nat = |N Operations 0, 1, 2: Nat +, x: Nat Nat -> Nat ... Equations for all N, M: Nat N x 0 = 0. N x (M + 1) = N + (N x M). ... Semantic functions: valuation: Numeral -> Nat digitVal: Digit -> Nat valuation[[d]] = digitVal[[d]] valuation[[n d]] = (2 x (valuation[[n]])) + digitVal[[d]] digitVal[[0]] = 0 digitVal[[1]] = 1 ----------------------------- *** remarks (see Watt, page 52 at bottom) - meaning of each phrase is a mathematical thing (its denotation) - for each category in the grammar, introduce a semantic function - specify semantic function by several equations, each uses the meaning of its subphrases (this what is meant by compositionality) - this style, and some math techniques defines the denotational approach. *** translation into SML can translate this fairly directly into SML. ----------------------------- (* Implementation in SML *) (* Abstract syntax trees *) datatype Digit = d0 | d1; datatype Numeral = digit of Digit | numDigit of Numeral*Digit; (* Semantic Domains: Nat represented by positive SML ints *) type Nat = int; (* problem: finiteness *) (* Semantic Functions *) fun digitVal((d0)) = 0 | digitVal((d1)) = 1; fun valuation((digit d)) = digitVal(d) | valuation((numDigit(n,d))) = (2*(valuation((n)))) + digitVal((d)); (* calculations *) (* making an abstract syntax tree *) numDigit(digit(d1),d0); valuation ((numDigit(digit(d1),d0))); --------------------------- the only problem with this encoding is that we can't say what the signature of digitVal and valuation are so a better encoding uses the SML module system (chapter 7 of Paulson) explain the stuff below. ---------------------------- (* Implementation in SML *) (* Abstract syntax trees *) datatype Digit = d0 | d1; datatype Numeral = digit of Digit | numDigit of Numeral*Digit; (* Semantic Domains: *) type Nat = int (* Semantic Functions *) signature NumSemSig = sig val digitVal: Digit -> Nat val valuation: Numeral -> Nat end; structure NumSem: NumSemSig = struct fun digitVal((d0)) = 0 | digitVal((d1)) = 1; fun valuation((digit d)) = digitVal(d) | valuation((numDigit(n,d))) = (2*(valuation((n)))) + digitVal((d)); end; (* calculations *) (* making an abstract syntax tree *) numDigit(digit(d1),d0); NumSem.valuation ((numDigit(digit(d1),d0))); ---------------------------- ** where are we going with this? chapter numbers from Watt's book principles, stores, environments (chapter 3) applications, mini-Cecil, I/O, reasoning (chapter 4) theory, domains and the fixpoint construction (chapter 5)