Algebraic Semantics Abstract type whose values are lists of Algebraic semantics involves the algebraic integers: specification of data and language constructs. Sorts = { Integer, Boolean, List }. Function symbols with their signatures: Foundations based on abstract algebras. zero : Integer Basic idea one : Integer • Name the sorts of objects and the operations plus ( _ , _ ) : Integer, Integer ! Integer on the objects. minus ( _ , _ ) : Integer, Integer ! Integer • Use algebraic axioms to describe their characteristic properties. true : Boolean false : Boolean An algebraic specification contains two parts: emptyList : List and equations. cons ( _ , _ ) : Integer, List ! List A signature # of an algebraic specification head ( _ ) : List ! Integer is a pair where tail ( _ ) : List ! List • Sorts is a set containing names of sorts. empty? ( _ ) : List ! Boolean • Operations is a family of function symbols length ( _ ) : List ! Integer indexed by the functionalities of the operations represented by the function symbols.

Chapter 12 1 Chapter 12 2

Family of operations decomposes: Module Representation OprBoolean = { true, false } OprInteger,Integer!Integer = { plus, minus } • Decompose definitions into relatively small OprList!Integer = { head, length } components.

Equations constrain the operations to indicate • Import the signature and equations of one the appropriate behavior for the operations. module into another. head (cons (m, s)) = m, empty? (emptyList) = true • Define sorts and functions to be either exported or hidden. empty? (cons (m, s)) = false.

Each stands for a closed assertion: • Modules can be parameterized to define generic abstract data types. "m:Integer, "s:List [head (cons (m, s)) = m]. empty? (emptyList) = true "m:Integer, "s:List [empty? (cons (m, s))= false].

Chapter 12 3 Chapter 12 4 A Module for Truth Values variables module Booleans b, b1, b2 : Boolean exports equations sorts Boolean [B1] and (true, b) = b operations [B2] and (false, true) = false true : Boolean [B3] and (false, false) = false false : Boolean [B4] not (true)= false errorBoolean : Boolean [B5] not (false) = true not ( _ ) : Boolean Boolean ! [B6] or (b1, b2) = not (and (not (b1), not (b2))) and ( _ , _ ) : [B7] implies (b1, b2) = or (not (b1), b2) Boolean, Boolean Boolean ! [B8] xor (b1, b2) = or ( _ , _ ) : and (or(b1,b2),not(and(b1,b2))) Boolean, Boolean Boolean ! [B9] eq? (b1, b2) = not (xor (b1, b2)) implies ( _ , _ ) : end Booleans Boolean,Boolean ! Boolean eq? ( _ , _ ) : Note module syntax Boolean, Boolean ! Boolean end exports A conditional equation has the form operations lhs=rhs when lhs =rhs , lhs =rhs , ..., lhs =rhs . xor ( _ , _ ) : Boolean, Boolean ! Boolean 1 1 2 2 n n

Chapter 12 5 Chapter 12 6

A Module for Natural Numbers variables m, n : Natural module Naturals equations imports Booleans [N1] 1 = succ (0) exports [N2] 10 = succ (succ (succ (succ (succ ( sorts Natural succ (succ (succ (succ (succ (0)))))))))) operations [N3] add (m, 0) = m 0 : Natural [N4] add (m, succ (n)) = succ (add (m, n)) 1 : Natural 10 : Natural [N5] sub (0, succ(n)) = errorNatural errorNatural : Natural [N6] sub (m, 0) = m succ ( _ ) : Natural ! Natural [N7] sub(succ(m),succ(n)) =sub(m,n) add ( _ , _ ) : Natural, Natural ! Natural [N8] mul (m, 0) = 0 when m!errorNatural sub ( _ , _ ) : Natural, Natural ! Natural [N9] mul (m, 1) = m mul ( _ , _ ) : Natural, Natural ! Natural [N10] mul (m, succ(n)) = add (m, mul (m, n)) div ( _ , _ ) : Natural, Natural ! Natural [N11] div (m, 0) = errorNatural eq? ( _ , _ ) : Natural, Natural Boolean ! [N12] div (0, succ (n)) = 0 when n!errorNatural less? ( _ , _ ) : [N13] div (m, succ (n)) = Natural, Natural ! Boolean if ( less? (m, succ (n)), greater?( _ , _ ) : 0, Natural, Natural ! Boolean succ(div(sub(m,succ(n)),succ(n)))) end exports

Chapter 12 7 Chapter 12 8 [N14] eq? (0, 0) = true [N15] eq? (0, succ (n)) = false Conditions are Necessary when n!errorNatural [N16] eq? (succ (m), 0) = false Use [N8] and ignore the condition: when m!errorNatural [N17] eq? (succ (m), succ (n)) = eq? (m, n) 0 = mul(succ(errorNatural),0) [N18] less? (0, succ (m)) = true = mul(errorNatural,0) when m!errorNatural = errorNatural. [N19] less? (m, 0) = false when m!errorNatural and [N20] less? (succ (m), succ (n)) = less? (m, n) succ(0) = succ(errorNatural) = errorNatural, [N21] greater? (m, n) = less? (n, m) succ(succ(0)) = end Naturals succ(errorNatural) = errorNatural, and so on. All operations propagate errors succ (errorNatural) = errorNatural, Conditions are needed when variable(s) on the left disappear on the right. sub (div(0,0), succ(0)) = errorNatural, not (errorBoolean) = errorBoolean, and eq? (0, succ (errorNatural)) = errorBoolean.

Chapter 12 9 Chapter 12 10

Constructors A Module for Characters

• No equations for 0 and succ module Characters • Terms 0, succ(0), succ(succ(0)), ... not equal importsBooleans, Naturals exports • These plus errorNatural can be viewed as characterizing the natural numbers, the sorts Char individuals defined by the module. operations eq? ( _ , _ ) : Char, Char ! Boolean • Initial algebraic semantics letter? ( _ ) : Char ! Boolean digit? ( _ ) : Char ! Boolean • No confusion property ord ( _ ) : Char ! Natural • No junk property char-0 : Char char-1 : Char : : char-9 : Char char-a : Char : : char-z : Char errorChar : Char end exports

Chapter 12 11 Chapter 12 12 variables Parameterized Module and Instantiations c, c1, c2 : Char module Lists equations imports Booleans, Naturals [C1] ord (char-0) = 0 [C2] ord (char-1) = succ (ord (char-0)) parameters Items [C3] ord (char-2) = succ (ord (char-1)) sorts Item : : : operations [C11] ord (char-a) = succ (ord (char-9)) errorItem : Item [C12] ord (char-b) = succ (ord (char-a)) eq? : Item, Item ! Boolean : : : variables [C36] ord (char-z) = succ (ord (char-y)) a, b, c : Item equations [C37] eq? (c1, c2) = eq? (ord (c1), ord (c2)) eq? (a,a) = true when a!errorItem [C38] letter? (c) = eq? (a,b) = eq? (b,a) and (not (greater? (ord (char-a), ord (c))), implies(and(eq?(a,b),eq?(b,c)), not (greater? (ord (c), ord (char-z)))) eq?(a,c))=true [C39] digit? (c) = when a!errorItem, and (not (greater? (ord (char-0), ord (c))), b!errorItem, not (greater? (ord (c) ord (char-9)))) c!errorItem end Characters end Items

Chapter 12 13 Chapter 12 14

exports equations sorts List [S1] concat (null, s) = s

operations [S2] concat(cons(i,s1),s2) = null : List cons(i,concat(s1, s2)) errorList : List [S3] equal? (null, null) = true cons ( _ , _ ) : Item, List ! List concat ( _ , _ ) : List, List ! List [S4] equal? (null, cons (i, s)) = false length ( _ ) : List ! Natural when s!errorList, i!errorItem equal? ( _ , _ ): List, List Boolean ! [S5] equal? (cons (i, s), null) = false mkList ( _ ) : Item List ! when s!errorList, i!errorItem end exports [S6] equal? (cons (i1, s1), cons (i2, s2)) = variables and(eq?(i1, i2), equal?(s1, s2)) i, i1, i2 : Item [S7] length (null) = 0 s, s , s : List 1 2 [S8] length (cons (i, s)) = succ (length (s)) when i!errorItem [S9] mkList (i) = cons (i, null) end Lists

Chapter 12 15 Chapter 12 16 Instantiations module Strings imports Booleans, Naturals, Characters, module Files instantiation of Lists importsBooleans, Naturals, bind Items using Char for Item instantiation of Lists using errorChar for errorItem bind Items using eq? for eq? using Natural for Item rename using String for List using errorNatural for errorItem using nullString for null using eq? for eq? using mkString for mkList rename using File for List using strEqual for equal? using emptyFile for null using errorString for errorList using mkFile for mkList using errorFile for errorList exports exports sorts String sorts File operations operations string-to-natural ( _ ) : empty? ( _ ) : File ! Boolean end exports String ! Boolean, Natural variables f : File end exports equations [F1] empty? (f) = equal? (f, emptyFile) end Files

Chapter 12 17 Chapter 12 18

variables A Module for Finite Mappings c : Char b : Boolean module Mappings n : Natural s : String imports Booleans equations parameters Entries sorts Domain, Range [Str1] string-to-natural (nullString) = operations [Str2] string-to-natural (cons (c, s)) = equals ( _ , _ ) : if ( and (digit? (c), b), Domain,Domain ! Boolean , errorRange : Range ) variables when = string-to-natural (s) a,b,c : Domain end Strings equations equals (a,a) = true when a!errorDomain Expression in [Str2]: equals (a,b) = equals (b,a) ((ord(c) – ord(char-0)) • 10length(s)) + n implies (and (equals (a,b), equals (b,c)), equals (a,c)) = true when a, b, and c ! errorDomain end Entries

Chapter 12 19 Chapter 12 20 exports sorts Mapping A Store Structure operations emptyMap : Mapping module Stores errorMapping : Mapping imports Strings, Naturals, update( _ , _ , _ ) : Mapping,Domain,Range ! Mapping instantiation of Mappings apply ( _ , _ ) : bind Entries Mapping, Domain ! Range using String for Domain end exports using Natural for Range variables using strEqual for equals m : Mapping using errorString for errorDomain d, d1, d2 : Domain using errorNatural for errorRange r : Range rename using Store for Mapping equations using emptySto for emptyMap [M1] apply (emptyMap, d) = errorRange using updateSto for update [M2] apply (update(m, d1, r), d2) = r using applySto for apply when equals(d1,d2) = true, m!errorMapping end Stores [M3] apply (update(m, d1, r), d2) = apply(m, d2) when equals(d1,d2)=false, r!errorRange end Mappings

Chapter 12 21 Chapter 12 22

Mathematical Foundations module Nats imports Bools Simplify modules. exports sorts Natural module Bools operations exports 0 : Natural sorts Boolean succ ( _ ) : Natural ! Natural operations add ( _ , _ ) : Natural, Natural ! Natural true : Boolean end exports false : Boolean variables not ( _ ) : Boolean ! Boolean m, n : Natural end exports equations equations [N1] add (m, 0) = m [B1] not (true) = false [N2] add (m, succ (n)) = succ (add (m, n)) [B2] not (false) = true end Nats end Bools

Chapter 12 23 Chapter 12 24 Example: Ground terms of sort Boolean in Ground Terms Bools true, not(true), Function symbols used to construct terms not(not(true)), not(not(not(true))), ... that stand for the objects of the sorts in the signature. false, not(false), not(not(false)), … Ground terms of sort Natural in Nats: Defn: 0, succ(0), succ(succ(0)), … For a given signature # = , the set of ground terms TS of sort S is add(0,0), add(0,succ(0)), defined inductively: add(succ(0),0), add(succ(0),succ(0)), 1. All constants of sort S in Operations are add(0,succ(succ(0))), ground terms (in T ). S add(succ(succ(0)),0), 2. For every function symbol f : S1,…,Sn ! S add(0,succ(succ(succ(0)))), in Operations, if t1,…,tn are ground terms of sorts S1,…,Sn, respectively, add(succ(succ(succ(0))),0), then f(t1,…,tn) is a ground of sort S add(succ(0),succ(succ(0)), where S1,…,Sn,S$Sorts. :

On the basis of the signature only (no equations), the ground terms must be mutually distinct.

Chapter 12 25 Chapter 12 26

#-Algebras Let # = be a signature where Algebraic specifications deal with syntax. • Sorts is a set of sort names and • Operations is a set of function symbols of the Semantics is provided by defining algebras that form f : S1, …, Sm ! Sm+1 where each Si $ serve as models of the specifications. Sorts.

Heterogeneous or Many-sorted Algebras: A #-algebra A consists of: A set of operations acting on a collection of 1. A collection of sets { SA | S$Sorts }, sets. the carrier sets

2. A collection of functions { fA | f$Operations } Defn: For a given signature #, an algebra A is with the functionality a #-algebra under the following circumstances: fA : (S1) A, …, (Sm) A ! SA for each f : S1, …, Sm ! S in Operations. • There is a one-to-one correspondence between the carrier sets of A and the sorts of # #-algebras are called heterogeneous or many- sorted algebras because they may contain • There is a one-to-one correspondence objects of more than one sort. between the constants and functions of A and the operation symbols of # so that those constants and functions are of the appropriate sorts and functionalities.

Chapter 12 27 Chapter 12 28 Defn: The term algebra T# for a signature Example # = is constructed as The carrier set for the term algebra T follows. Carrier sets { ST# | S$Sorts } are # defined by: constructed from the module Bools contains all the ground terms from the signature, including 1. For each constant c of sort S in # we have a “true”, “not(true)”, “not(not(true))”, ... corresponding constant “c” in ST#. “false”, “not(false)”, “not(not(false))”, .... 2. For each function symbol f : S1,...,Sn ! S in

# and any n elements t1$(S1) T#, … ,tn$(Sn) The function notT maps “true” to “not(true)”, T , the term “f(t1, ..., tn)” belongs to the carrier # # maps “not(true)” to “not(not(true))”, and so forth. set ST#.

For each function symbol f : S1,...,Sn ! S in # The carrier set is infinite. and any n elements t1$(S1)T#, … ,tn$(Sn) T#, define fT# by fT#(t1, ..., tn) = “f(t1, ..., tn)”. Also, “false” ! “not(true)”

The elements of the carrier sets of T# consist We have not accounted for the equations of strings of symbols chosen from a set and what properties they enforce in an containing the constants and function symbols algebra. of # together with the special symbols “(”, “)”, and “,”.

Chapter 12 29 Chapter 12 30

Defn: For a signature # and a #-algebra A, the Definition: Let Spec = <#,E> be a specification with signature and equations E. evaluation function evalA : T# ! A from # ground terms to values in A is defined as: The congruence %E determined by E on T# is the smallest relation satisfying the properties: evalA("c") = cA for constants c, and 1. Variable Assignment: Given an equation evalA("f(t1,…,tn)") = fA(evalA(t1), …, evalA(tn)) lhs = rhs in E that contains variables v1,..,vn where each term ti is of sort Si for the and given any ground terms t1,..,tn from T# symbol f : S1,…,Sm !S in Operations. of the same sorts as the respective variables, lhs[v1 | ! t1,…,vn | ! tn] %E A Congruence from the Equations rhs[v1 | ! t1,...,vn | ! tn] The function symbols and constants create where vi | ! ti indicates substituting the a set of ground terms. ground term ti for the variable vi. If equation is conditional, the condition must The equations of a specification generate be valid after variable assignment is carried a congruence % on the ground terms. out on it. 2. Reflexive: For every ground term t$T#, A congruence is an equivalence relation with t %E t. an additional “” property. 3. Symmetric: For any ground terms t1, t2$T#, t1 %E t2 implies t2 %E t1.

Chapter 12 31 Chapter 12 32 4. Transitive: For any terms t1, t2, t3$T#, (t1 %E t2 and t2 %E t3) implies t1 %E t3. Ground terms for Bools module: true % not(false) % not(not(true)) 5. Substitution Property: If t1 %E t1',…,tn %E tn' % not(not(not(false))) % ... and f : S1,…,Sn ! S is any function symbol false % not(true) % not(not(false)) in #, then f(t1,…,tn) %E f(t1',…,tn'). % not(not(not(true))) % ...

Generate an equivalence relation from Sample Proof equations: add(succ(0),succ(0)) • Take every ground instance of all the % succ(add(succ(0),0)) using [N2] and equations as a basis. [m| !succ(0), n| !0] • Allow any derivation using properties reflexive, symmetric, and transitive and the % succ(succ(0)) using [N1] and substitution rule that each function symbol [m | ! succ(0)]. preserves equivalence when building ground terms. Defn: If Spec = <#,E>, a #-algebra A is a model of Spec if for all ground terms t1 and t2, t1 %E t2 implies evalA(t1) = evalA(t2).

Chapter 12 33 Chapter 12 34

Example: A = <{ {off,on} }, {off, on, switch}> Construct a particular #-algebra, called the where off and on are constants initial algebra, that is guaranteed to exist, and switch is defined by and take it to be the meaning of the switch(off) = on specification Spec. switch(on) = off. Let # be the signature of Bools. Quotient Algebra A #-algebra A: Build the quotient algebra Q from the term Boolean = {off,on} is the carrier set A algebra T# of a specification by factoring Operation of # Functions of A out congruences. true : Boolean trueA = on : BooleanA false : Boolean falseA = off : BooleanA Defn: Let <#,E> be a specification with not : Boolean ! Boolean # = . notA= switch : BooleanA!BooleanA If t is a term in T#, we represent its congruence class as [t] = { t' | t %E t' }. For example, not(true) % false and So [t] = [t'] if and only if t %E t'. eval ("not(true)") = not (eval ("true")) A A A Carrier sets = { (S)T# | S$Sorts }. = notA(trueA) = switch(on) = off, and evalA("false") = off.

Chapter 12 35 Chapter 12 36 A constant c becomes congruence class [c]. The function notQ:

Functions in the term algebra define functions notQ ([false]) = [not(false)] = [true], and in the quotient algebra: notQ ([true]) = [not(true)] = [false]. Given a function symbol f : S1,...,Sn ! S in #, fQ([t1],…,[tn]) = [f(t1,..,tn)] for any terms ti : Si, This quotient algebra is an initial algebra for with 1"i"n, from the appropriate carrier sets. Bools. Initial algebras are not necessarily unique.

The function fQ is well-defined: For example, the algebra = <{off, on}, {off, on, switch}> t1 %E t1', ..., tn %E tn' A is also an initial algebra for Bools. implies fQ(t1,..,tn) %E fQ(t1',..,tn') by the Substitution Property for congruences. An initial algebra is finest-grained: It equates For Bools: only those terms required to be equated, and so its carrier sets contain as many elements trueQ = [true] and falseQ = [false]. as possible. The congruence class [true] contains “true”, “not(false)”,“not(not(true))”, ... Using this procedure for developing the term algebra and then the quotient algebra, we The congruence class [false] contains can always guarantee that at least one initial “false”, “not(true)”, “not(not(false))”, .... algebra exists for any specification.

Chapter 12 37 Chapter 12 38

Homomorphisms h is an isomorphism Functions between #-algebras that preserve If h is a #- from A to B and the operations are called #-. the inverse of h is a #-homomorphism from Used to compare and contrast algebras that B to A. act as models of specifications. Defn: Suppose that A and B are #-algebras Apart from renaming carrier sets, constants, for a given signature # = . and functions, the two algebras are exactly h is a #-homomorphism if it maps carrier the same. sets of A to carrier sets of B and constants and functions of A to constants and functions of B, so that the behavior of constants and functions is preserved. Defn: A #-algebra I in the class of all #-algebras serving as models of a specification with signature is called initial if for any h consists of a collection { hS | S$Sorts } of # functions hS : SA ! SB for S$Sorts such that #-algebra A in the class, there is a unique homomorphism h : I ! A. hS(cA) = cB for each constant symbol c : S, and hS(fA (a1,…,an)) = fB (hS1(a1),…,hSn(an)) for each function symbol f : S1,...,Sn ! S in # and any n elements a1$(S1)A,…,an$(Sn)A.

Chapter 12 39 Chapter 12 40 Defn: Let < ,E> be a specification, let be The quotient algebra Q for a specification is # Q an initial algebra. the quotient algebra for <#,E>, and let B be an arbitrary model of the specification. 1. If homomorphism from Q to a #-algebra B is For any #-algebra A that acts as a model not onto, then B contains junk (values that of the specification, there is a unique do not correspond to terms constructed from #-homomorphism from Q to A. signature). Q B The function evalA : T# ! A induces a h #-homomorphism h from Q to A using the definition: h([t]) = evalA (t) for each t$T#.

Any algebra isomorphic to Q is also an initial h is not onto algebra.

So since the quotient algebra Q and the algebra A = <{off, on}, {off, on, switch}> are isomorphic, A is also an initial algebra for Bools.

Chapter 12 41 Chapter 12 42

The positive integers above 32767 must be confusion. 2. If homomorphism from Q to B is not one- to-one, then B exhibits confusion (two When mapping an infinite carrier set onto a different values in quotient algebra finite machine, confusion must occur. correspond to same term in B). Q Consistency and Completeness B Suppose we want to add a predecessor h operation to naturals by importing Naturals (original version) and defining a predecessor function pred.

module Predecessor1 h is not one-to-one imports Boolean, Naturals exports Example operations Consider the quotient algebra for Nats with the pred ( _ ) : Natural ! Natural infinite carrier set end exports [0], [succ(0)], [succ(succ(0))], …. variables Suppose that we have a 16-bit computer for n : Natural which the integers consist of the following set equations of values: [P1] pred (succ (n)) = n { -32768, -32767, ..., -1, 0, 1, 2, ..., 32766, 32767 }. end Predecessor1 The negative integers are junk with respect to Naturals is a subspecification of Predecessor1 Nats since they cannot be images of any of since the signature and equations of the natural numbers.

Chapter 12 43 Chapter 12 44 Predecessor1 include the signature and equations of Naturals. The new congruence class [pred(0)] is not The first equation specifies the predecessor congruent to 0 or any of the successors of 0. by subtracting one, and the second equation is carried over from the “fix” for Predecessor1. We say that [pred(0)] is junk and that In the module Naturals, we have the Predecessor1 is not a complete extension of Naturals. congruence classes: We can resolve this problem by adding the [errorNatural], [0], [succ(0)], equation [P2] pred(0) = 0 (or [P2] pred(0) = [succ(succ(0))], .... errorNatural). With the new module Predecessor2, Suppose that we define another predecessor pred(0) = sub(0,succ(0)) module in the following way: = errorNatural by [P1] and [N5], and module Predecessor2 pred(0) = 0 by [P2]. imports Boolean, Naturals So we have reduced the number of exports congruence classes, since [0] = [errorNatural]. operations pred ( _ ) : Natural ! Natural Because this has introduced confusion, we end exports say that Predecessor2 is not a consistent variables extension of Naturals. n : Natural equations [P1] pred (n) = sub (n, succ (0)) [P2] pred (0) = 0 end Predecessor2

Chapter 12 45 Chapter 12 46

Using Algebraic Specifications Defn: Let Spec be a specification with signature Data Abstraction # = and equations E. 1. Information Hiding: Compiler should Suppose SubSpec is a subspecification of ensure that the user of an ADT does not Spec with sorts SubSorts (a subset of Sorts) have access to the representation (of and equations SubE (a subset of E). values) and implementation (of operations) Let T and SubT represent the terms of Sorts of an ADT. and SubSorts, respectively. 2. Encapsulation: All aspects of specification • Spec is a complete extension of SubSpec and implementation of an ADT should be if for every sort S in SubSorts and every contain in one or two syntactic unit(s) with a term t1 in T, there exists a term t2 in SubT well-defined interface to the users of the ADT. such that t1 and t2 are congruent with respect to E. Examples: Ada package Modula module • Spec is a consistent extension of Classes in OOP SubSpec if for every sort subS in SubSorts and all terms t1 and t2 in T, t1 and t2 are 3. Generic types (parameterized modules): congruent with respect to E if and only if t1 A way of defining an ADT as a template and t2 are congruent with respect to SubE. without specifying the nature of all its components. A generic type is instantiated when the properties of its missing component values are provided.

Chapter 12 47 Chapter 12 48 A Module for Unbounded Queues Properties of Queues Start by giving the signature of a specification Define the characteristic properties of the of queues of natural numbers. queue ADT by describing informally what each operation does, for example: module Queues imports Booleans, Naturals • The function isEmptyQ(q) returns true if and only if the queue q is empty. exports sorts Queue • The function frontQ(q) returns the natural operations number in the queue that was added earliest newQ : Queue without being deleted yet. errorQueue : Queue • If q is an empty queue, frontQ(q) is an error addQ ( _ , _ ) : Queue, Natural ! Queue value. deleteQ ( _ ) : Queue ! Queue frontQ ( _ ) : Queue ! Natural isEmptyQ ( _ ) : Queue ! Boolean The descriptions are ambiguous, depending on end exports terms that have not been defined—for end Queues example, “empty” and “earliest”. Cannot assume any properties of the One may be tempted to define the meaning of operations other than their basic syntax. the operations in terms of an implementation, but this defeats the whole intent of data This module could be specifying stacks instead abstraction, which is to separate logical of queues. properties of data objects from their concrete realization.

Chapter 12 49 Chapter 12 50

A more formal approach to specifying the Implementing Queues properties of an ADT is through a set of axioms in the form of module equations that as Unbounded Arrays relate the operations to each other. Assuming that the axioms correctly specify the variables concept of a queue, use them to verify that an q : Queue implementation is correct. m : Natural Realization of an abstract data type: equations • a representation of the objects of the type [Q1] isEmptyQ (newQ) = true • implementations of the operations [Q2] isEmptyQ (addQ (q,m)) = false when q!errorQueue, m!errorNatural • representation function & that maps terms in the model onto the abstract objects so [Q3] delete (newQ) = newQ that the axioms are satisfied. [Q4] deleteQ (addQ (q,m)) = if ( isEmptyQ (q), newQ, addQ (deleteQ (q),m)) Plan when m!errorNatural Represent queues as arrays with two pointers, [Q5] frontQ (newQ) = errorNatural one to the front of the queue and one to the end. [Q6] frontQ (addQ (q,m)) = if ( isEmptyQ (q), m, frontQ (q) ) when m!errorNatural

Chapter 12 51 Chapter 12 52 A Module for Unbounded Arrays Implementation of the ADT Queue using the ADT Array has the following set of triples as module Arrays its objects: imports Booleans, Naturals ArrayQ = exports { | arr:Array, f,e:Natural, and f"e }. sorts Array operations newArray : Array Operations over ArrayQ are defined as errorArray : Array follows: assign(_,_,_) : Array,Natural,Natural!Array access ( _ , _ ) : Array, Natural ! Natural [AQ1] newAQ= end exports [AQ2] addAQ (, m) = variables arr: Array i, j, m : Natural [AQ3] deleteAQ () = equations if ( f = e, , ) [A1]access (newArray, i) = errorNatural [AQ4] frontAQ () = [A2]access (assign (arr, i, m), j) = if ( f = e, errorNatural, if ( i = j, m, access (arr, j) ) access(arr,f)) when m!errorNatural end Arrays [AQ5] isEmptyAQ () = (f = e) when arr!errorArray

Chapter 12 53 Chapter 12 54

Array queues are related to the abstract Under the homomorphism, the five equations queues by a homomorphism that define operations for the array queues map into five equations describing properties of & : {ArrayQ,Natural,Boolean} ! abstract queues. {Queue,Natural,Boolean}, defined on the objects and operations of the [D1] newQ = &(newArray,0,0) sorts. [D2] addQ ( (arr,f,e), m) = Use symbolic terms “&(arr,f,e)” to represent & abstract queue objects in Queue. &(assign(arr,e,m),f,e+1) For : ArrayQ, m : Natural, [D3] deleteQ (&(arr,f,e)) = and b : Boolean, if ( f = e, &(arr,f,e), &(arr,f+1,e) ) () = (arr,f,e) when f"e & & [D4] frontQ (&(arr,f,e)) = & () = errorQueue when f>e if ( f = e, errorNatural, access(arr,f)) & (m) = m & (b) = b [D5] isEmptyQ (&(arr,f,e)) = (f = e) & (newAQ) = newQ & (addAQ) = addQ & (deleteAQ) = deleteQ & (frontAQ) = frontQ & (isEmptyAQ) = isEmptyQ

Chapter 12 55 Chapter 12 56 Consider the image of [AQ2] under &. Lemma: For any queue &(a,f,e) constructed Assume [AQ2] using the operations of the implementation, f"e. addAQ (,m) = Proof: The only operations that produce queues are newQ, addQ, and deleteQ, the constructors in the signature. The proof is by Then addQ (&(arr,f,e),m) induction on the number of applications of = &(addAQ) (&(),&(m)>) these operations. = &(addAQ (,m)) Basis: Since newQ = &(newArray,0,0), f"e. = &(assign(arr,e,m),f,e+1), Induction Step: Suppose that &(a,f,e) has which is [D2]. been constructed with n applications of the operations and that f"e. The implementation is correct if its objects can Consider a queue constructed with one more be shown to satisfy the queue axioms [Q1] to application of these functions, for a total of [Q6] for arbitrary queues of the form q = n+1. &(arr,f,e) with f"e and arbitrary elements m of Natural, given the definitions [D1] to [D5] and Case 1: The n+1st operation is addQ. the equations for arrays. But addQ (&(a,f,e),m) = &(assign (a,f,m),f,e+1) has f"e+1.

Case 2: The n+1st operation is deleteQ. But deleteQ (&(a,f,e)) = if (f = e, &(arr,f,e), &(arr,f+1,e) ). If f=e, then f"e, and if f

Chapter 12 57 Chapter 12 58

The proof is an example of structural induction, induction that covers all of the ways Verification of Queue Axioms in which the objects of the data type may be Let q = &(a,f,e) be an arbitrary queue and let m constructed. be an arbitrary element of Natural.

Structural Induction: Suppose f1, f2, …, fn [Q1] isEmptyQ (newQ) are the operations that act as constructors = isEmptyQ (&(newArray,f,f)) by [D1] for an abstract data type S, and P is a = (f = f) = true by [D5]. property of values of sort S. If the truth of P for all arguments of sort S for [Q2] isEmptyQ (addQ (&(arr,f,e),m)) each f implies the truth of P for the results of i = isEmptyQ ( (assign(arr,e,m),f,e+1) all applications of fi that satisfy the syntactic & specification of S, it follows that P is true of by [D2] all values of the data type. = (f = e+1) = false, since f"e The basis case results from those by [D5] & lemma. constructors with no arguments. [Q3] deleteQ (newQ) For the verification of [Q4] as part of proving = deleteQ (&(newArray,f,f)) by [D1] the validity of this queue implementation, = &(newArray,f,f) = newQ extend & for the following values: by [D3] and [D1]. For any f : Natural and arr : Array, &(arr,f,f) = newQ. This extension is consistent with definition [D1].

Chapter 12 59 Chapter 12 60 [Q4] deleteQ (addQ (&(arr,f,e), m)) [Q6] frontQ (addQ (&(arr,f,e), m)) = deleteQ (&(assign(arr,e,m),f,e+1)) by [D2] = frontQ (&(assign(arr,e,m),f,e+1)) by = &(assign(arr,e,m),f+1,e+1) by [D4]. [D2] = access (assign(arr,e,m), f) by [D4]. Case 1: f = e, that is, isEmptyQ (&(arr,f,e)) = true. Case 1: f = e, that is, isEmptyQ (&(arr,f,e)) = true. Then &(assign(a,e,m),f+1,e+1) = newQ by [D1]. Then access (assign(arr,e,m), f) Case 2: f < e, = access (assign (arr,e,m), e) =m by [A2]. that is, isEmptyQ (&(arr,f,e)) = false. Then &(assign(arr,e,m),f+1,e+1) Case 2: f < e, that is, isEmptyQ (&(arr,f,e)) = false. = addQ (&(arr,f+1,e), m) by [D2] = addQ (deleteQ (&(arr,f,e)), m) by [D3]. Then access (assign (arr,e,m), f) = access (arr,f) [Q5] frontQ (newQ) = frontQ (&(arr,f,e)) by [A2] and [D4]. = frontQ (&(newArray,f,f) by [D1] = errorNatural since f = f by [D4]. Since the six axioms for the unbounded queue ADT have been verified, the implementation via the unbounded arrays is correct.

Chapter 12 61 Chapter 12 62

ADTs As Algebras newQ isEmptyQ frontQ Recall that any signature # defines a #-algebra T# of all the terms over the signature, and that by taking the quotient algebra Q defined by the Boolean Queue Natural congruence based on the equations E of a specification, we get an initial algebra that serves as the finest-grained model of a deleteQ addQ specification <#,E>. Signature of Queues

Example: An instance of the Queue ADT has The signature of the Queue ADT defines a operations involving three sorts of term algebra T#, sometimes called a free word objects—namely, Natural, Boolean, and the algebra, formed by taking all legal type being defined, Queue. Some authors combinations of operations that produce designate the type being defined as the type Queue objects. of interest. In this context, a graphical The values in the sort Queue are those notation has been suggested to define the produced by the constructor operations. signature of the operations of the algebra. Example of terms in T#: newQ, addQ (newQ,5), and deleteQ (addQ (addQ (deleteQ (newQ),9),15)).

Chapter 12 63 Chapter 12 64 The term free for such an algebra means that For an n-ary operation f$S the operations are combined in any way and t1,t2,…,tn$ T#, satisfying the syntactic constraints, and that all let f ([t ],[t ],…,[t ]) = [f(t ,t ,…,t )]. such terms are distinct objects in the algebra. Q 1 2 n 1 2 n The resulting (quotient) algebra, also called The properties of an ADT are given by a set E of equations or axioms that define identities T#,E, is the abstract data type being defined. When manipulating the objects of the (quotient) among the terms of T#. algebra T#,E the normal practice is to use So the Queue ADT is not a free algebra, since representatives from the equivalence classes. the axioms recognize certain terms as being equal. Definition: A canonical or normal form for the terms in a quotient algebra is a set of For example: distinct representatives, one from each deleteQ (newQ) = newQ and equivalence class. deleteQ(addQ(addQ(deleteQ(newQ),9),15)) = addQ (newQ, 15). Lemma: For the Queue ADT T#,E each term is The equations define a congruence %E on the equivalent to the value newQ or a term of the free algebra of terms as described in section form 12.2. That equivalence relation defines a set of addQ(addQ(…addQ(addQ(newQ,m1),m2),…), equivalence classes that partitions T#. mn–1),mn) for some n#1 [ t ]E = { u$ T# | u %E t } where m1,m2,…,mn : Natural. For example, [ newQ ]E = { newQ, Proof: The proof is by structural induction. deleteQ(newQ), deleteQ(deleteQ(newQ)), … }. Basis: The only constant in T# is newQ, which The operations of the ADT can be defined on is in normal form. these equivalence classes before:

Chapter 12 65 Chapter 12 66

Induction Step: Consider a queue term t with more than one application of the constructors (newQ, addQ, deleteQ), and assume that any A canonical form for a ADT can be thought of term with fewer applications of the as an “abstract implementation” of the type. constructors can be put into normal form. Case 1: t = addQ(q,m) will be in normal form John Guttag [Guttag78b] calls this a direct when q, which has fewer constructors than implementation and represents it graphically t, is in normal form. as shown below.

Case 2: Consider t = deleteQ(q) where q is in newQ = newQ

normal form. addQ Subcase a: q = newQ. Then deleteQ(q) = addQ 8 newQ is in normal form. addQ (addQ (addQ (newQ, 3), 5), 8) =

Subcase b: q = addQ(p,m) where p is in normal addQ 5 form. Then deleteQ(addQ(p,m)) = if ( newQ 3 isEmptyQ(p), newQ, addQ(deleteQ(p),m)) The canonical form for an ADT provides an If p is empty, deleteQ(q) = newQ is in effective tool for proving properties about the normal form. type. If p is not empty, deleteQ(q) = addQ(deleteQ(p),m). Since deleteQ(p) has fewer constructors than t, it can be put into normal form, so that deleteQ(q) is in normal form. !

Chapter 12 67 Chapter 12 68 Lemma: The representation function & that Definition: A set of equations for an ADT is implements queues as arrays is an onto sufficiently complete if for each ground term function. f(t1,t2,…,tn) where f$Sel, the set of selectors, Proof: Since any queue can be written as there is an element u of a predefined type such newQ or as addQ(q,m), we need to handle that f(t ,t ,…,t ) E u. This condition means only these two forms. 1 2 n % there are sufficient axioms to make the By [D1], &(newArray,0,0) = newQ. derivation to u. Assume as an induction hypothesis that q = &(arr,f,e) for some array. Then by [D2], &(assign(arr,e,m),f,e+1) = addQ Theorem: The equations in the module (&(arr,f,e),m). Queues are sufficiently complete. Therefore, any queue is the image of some Proof: triple under the representation function &. ! 1. Every queue can be written in normal form as newQ or as addQ(q,m). Given an ADT with signature S, operations in 2. isEmptyQ(newQ) = true, S that produce element of the type of interest have already been called constructors. isEmptyQ(addQ(q,m)) = false, frontQ(newQ) Those operations in S whose range is an = errorNatural, and frontQ(addQ(q,m)) already defined type of “basic” values are = m or frontQ(q) (use induction). ! called selectors. The operations of S are partitioned into two disjoint sets, Con the set of constructors and Sel the set of selectors. The selectors for Queues are frontQ and isEmptyQ.

Chapter 12 69 Chapter 12 70

Abstract Syntax Example: Expressions and Algebraic Specifications Concrete Syntax: Points about abstract syntax: ::= • Only need to specify the meaning of the ::= + syntactic forms given by the abstract syntax, ::= - since this formalism furnishes all the essential syntactic constructs in the language. ::= ::= * • No harm arises from an ambiguous abstract ::= syntax since its purpose is not syntactic analysis . ::= ( ) Define a signature # that corresponds exactly • The abstract syntax of a programming to the BNF definition. language may take many different forms, depending on the semantic techniques that Each nonterminal becomes a sort in #, and are applied to it. each production becomes a function symbol whose syntax captures the essence of the These points raise questions concerning the production. nature of abstract syntax and its relation to the language defined by the concrete syntax.

Chapter 12 71 Chapter 12 72 The signature of the concrete syntax is given in the module Expressions. Consider the collection of #-algebras following this signature. module Expressions exports The term algebra T# is initial in the collection of sorts Expression, Term, Element, Identifier all #-algebras, meaning that for any #-algebra operations A, there is a unique homomorphism h : T# ! A. expr ( _ ) : Term ! Expression The elements of T# are terms constructed add ( _ , _ ) : using the function symbols in #. Expression, Term ! Expression Since this signature has no constants, assume sub ( _ , _ ) : a set of constants of sort Identifier and represent them as structures of the form ide(x) Expression, Term ! Expression containing atoms as the identifiers. term ( _ ) : Element ! Term Think of these structures as the tokens mul ( _ , _ ) : Term, Element ! Term produced by a scanner. elem ( _ ) : Identifier ! Element paren ( _ ) : Expression ! Element The expression “x * (y + z)” corresponds to end exports the following term in T#: end Expressions t = expr (mul (term (elem (ide(x))), paren (add (expr (term (elem (ide(y)))), The terminal symbols in the grammar are term (elem (ide(z))))))). “forgotten” in the signature since they are embodied in unique names of the function symbols.

Chapter 12 73 Chapter 12 74

Constructing such a term corresponds to Abstract Syntax parsing the expression. expr Concrete Syntax mul

term paren

add

ide expr term

term

elem

Chapter 12 75 Chapter 12 76 The concrete syntax of a programming Construct terms with the constructor function language coincides with the initial term algebra symbols in the AbstractExpressions module to of a specification with signature #. represent the abstract syntax trees. What does its abstract syntax correspond to? Consider the following algebraic specification These freely constructed terms form term of abstract syntax for the expression algebra A according to signature of language. AbstractExpressions. module AbstractExpressions A also serves as a model of the specification exports in the Expressions module; that is, A is a sorts AbsExpr, Symbol #-algebra: operations Expression = Term = Element = AbsExpr plus ( _ , _ ) : A A A AbsExpr, AbsExpr ! AbsExpr IdentifierA = { ide(x) | x : Symbol }. minus ( _ , _ ) : Operations: AbsExpr, AbsExpr ! AbsExpr exprA : AbsExpr ! AbsExpr defined by expr (e) = e times ( _ , _ ) : A AbsExpr, AbsExpr ! AbsExpr addA : AbsExpr, AbsExpr ! AbsExpr defined by addA (e1,e2) = plus(e1,e2) ide ( _ ) : Symbol ! AbsExpr end exports subA : AbsExpr, AbsExpr ! AbsExpr end AbstractExpressions defined by subA (e1,e2) = minus(e1,e2)

Use set Symbol of symbolic atoms as identifiers.

Chapter 12 77 Chapter 12 78

termA : AbsExpr ! AbsExpr = exprA (mulA (termA (ide(x)), defined by termA (e) = e parenA (addA (exprA (termA (ide(y))), mulA : AbsExpr, AbsExpr ! AbsExpr termA (ide(z)))))) defined by mulA (e1,e2) = times(e1,e2) = exprA (mulA (ide(x), parenA elemA : Identifier ! AbsExpr (addA (exprA (ide(y)), defined by elemA (e) = e ide(z)))))

parenA : AbsExpr ! AbsExpr = mulA (ide(x), addA (ide(y), ide(z))) defined by parenA (e) = e = times (ide(x), plus (ide(y), ide(z))), which represents the abstract syntax tree in A Under this interpretation of the symbols in #, that corresponds to the original expression this term t becomes a value in the #-algebra A: “x * (y + z)”. tA = (expr (mul (term (elem (ide(x))), paren (add (expr (term(elem (ide(y)))), Each version of abstract syntax is a #-algebra term (elem (ide(z))))))))A for the signature associated with the grammar = exprA (mulA (termA (elemA (ide(x))), that forms the concrete syntax of the language. parenA (addA (exprA (termA (elemA (ide(y)))), Any #-algebra serving as an abstract syntax is termA(elemA (ide(z))))))) a homomorphic image of T#, the initial algebra for the specification with signature #.

Chapter 12 79 Chapter 12 80 Confusion Algebraic Semantics for Wren Generally, #-algebras acting as abstract module WrenTypes syntax will contain confusion; the imports Booleans homomorphism from T# will not be one-to-one. exports sorts WrenType This confusion reflects the abstracting process: operations By confusing elements in the algebra, we are naturalType, booleanType : WrenType suppressing details in the syntax. programType, errorType : WrenType eq?( _ , _ ) : WrenType,WrenType ! Boolean The expressions “x+y” and “(x+y)”, although end exports distinct in the concrete syntax and in T#, are variables the same when mapped to plus(ide(x),ide(y)) t1, t2 : WrenType in A. equations Any #-algebra for the signature resulting from [Wt1]eq? (t1,t1) = true when t1!errorType the concrete syntax can serve as the abstract [Wt2]eq? (t1, t2) = eq? (t2,t1) syntax for some semantic specification of the [Wt3]eq? (naturalType, booleanType) = false language, but many such algebras will be so [Wt4]eq? (naturalType, programType) = false confused that the associated semantics will be trivial or absurd. [Wt5]eq? (naturalType, errorType) = false [Wt6]eq? (booleanType, programType) = false The task of the semanticist is to choose an [Wt7]eq? (booleanType, errorType) = false appropriate -algebra that captures the # [Wt8]eq? (programType, errorType) = false organization of the language in such a way end WrenTypes that appropriate semantics can be attributed to it.

Chapter 12 81 Chapter 12 82

module WrenValues equations imports Booleans, Naturals [Wv1] eq? (x, x) = true when x!errorValue exports sorts WrenValue [Wv2] eq? (x, y) = eq? (y,x) operations [Wv3] eq? (wrenValue(m), wrenValue(n)) wrenValue ( _ ) : Natural ! WrenValue = eq? (m,n) wrenValue ( _ ) : Boolean ! WrenValue [Wv4] eq? (wrenValue(b1), wrenValue(b2)) errorValue : WrenValue = eq? (b1,b2) eq?( _ , _ ) : WrenValue,WrenValue!Boolean [Wv5] eq? (wrenValue(m), wrenValue(b)) = false end exports when m!errorNatural, b!errorBoolean variables [Wv6] eq? (wrenValue(m), errorValue) = false x, y : WrenValue when m ! errorNatural m, n : Natural [Wv7] eq? (wrenValue(b), errorValue) = false b, b1, b2 : Boolean when b ! errorBoolean end WrenValues

Chapter 12 83 Chapter 12 84 Abstract Syntax for Wren astIfThen ( _ , _ ) : Expr, CmdSeq ! Command module WrenASTs astIfElse( _ , _ , _ ) : Expr,CmdS,CmdS ! Cmd imports Naturals, Strings, WrenTypes astAddition ( _ , _ ) : Expr, Expr ! Expr exports astSubtraction ( _ , _ ) : Expr, Expr ! Expr sortsWrenProgram, Block, DecSeq, astMultiplication ( _ , _ ) : Expr, Expr ! Expr Declaration, CmdSeq, Cmd, Expr, Ident astDivision ( _ , _ ) : Expr, Expr ! Expr operations astEqual ( _ , _ ) : Expr, Expr ! Expr astWrenProg ( _ , _ ) : Ident, Block ! WrenProg astNotEqual ( _ , _ ) : Expr, Expr ! Expr astBlock ( _ , _ ) : DecSeq, CmdSeq ! Block astLessThan ( _ , _ ) : Expr, Expr ! Expr astDecs ( _ , _ ) : Declaration, DecSeq ! DecSeq astLessThanEqual ( _ , _ ) : Expr, Expr ! Expr astEmptyDecs : DecSeq astGreaterThan ( _ , _ ) : Expr, Expr ! Expr astDec ( _ , _ ) : Ident, WrenType ! Declaration astGreaterThanEqual ( _ , _ ) : Expr, Expr ! Expr astCmds ( _ , _ ) : Cmd, CmdSeq ! CmdSeq astVariable ( _ ) : Ident ! Expr astOneCmd ( _ ) : Command ! CmdSeq astNaturalConstant ( _ ) : Natural ! Expr astRead ( _ ) : Ident ! Command astIdent ( _ ) : String ! Ident astWrite ( _ ) : Expr ! Command end exports astAssign ( _ , _ ) : Ident, Expr ! Command end WrenASTs astSkip : Command astWhile ( _ , _ ) : Expr, CmdSeq ! Command

Chapter 12 85 Chapter 12 86

A Type Checker for Wren operations module WrenTypeChecker typeExpr : Expr, SymTab ! WrenType importsBooleans, WrenTypes, WrenASTs, variables instantiation of Mappings block : Block bind Entries using String for Domain decs : DecSeq using WrenType for Range dec : Declaration using eq? for equals cmds, cmds1, cmds2 : CmdSeq using errorString for errorDomain cmd : Command using errorType for errorRange rename using SymbolTable for Mapping expr, expr1, expr2 : Expr using nullSymTab for emptyMap type:WrenType exports symtab, symtab1 : SymbolTable operations m : Natural check ( _ ) : WrenProgram ! Bool name : String check ( _ , _ ) : Block, SymTab ! Bool b, b1, b2 : Boolean check ( _ , _ ) : DecSeq, SymTab ! Bool,SymTab check( _ , _ ) : Declaration,SymTab ! Bool,SymTab check ( _ , _ ) : CmdSeq, SymTab ! Bool check ( _ , _ ) : Command, SymTab ! Bool end exports

Chapter 12 87 Chapter 12 88 equations [Tc6] check (astCmds (cmd, cmds), symtab) [Tc1] = and (check (cmd, symtab), check (astWrenProgram (astIdent (name), block)) check (cmds, symtab)) = check(block, update(nullSymTab,name,progType) [Tc7] check (astOneCmd (cmd), symtab) [Tc2] = check (cmd, symtab) check (astBlock (decs, cmds), symtab) = and (b1,b2) [Tc8] when =check (decs, symtab) check (astRead (astIdent (name)), symtab) b2 = check (cmds, symtab1) = eq?(apply (symtab, name), naturalType) [Tc3] [Tc9] check (astDecs (dec, decs), symtab) check (astWrite (expr, symtab) = = eq? (typeExpr (expr, symtab), when = check (dec, symtab) naturalType) =check(decs, symtab1) [Tc10] [Tc4] check(astAssign (astIdent (name), expr), symtab) check (astEmptyDecs, symtab) = eq? (apply(symtab, name), = typeExpr (expr, symtab)) [Tc5] [Tc11] check (astDec (astIdent (name), type), symtab) check (astSkip, symtab) = if ( apply (symtab, name) = errorType, = true , )

Chapter 12 89 Chapter 12 90

[Tc12] [Tc19] check (astWhile (expr, cmds), symtab) typeExpr (astEqual (expr1, expr2), symtab) = if (eq? (typeExpr (expr, symtab), = if (and(eq?(typeExpr(expr1,symtab),natType), booleanType), eq?(typeExpr(expr2,symtab),natType)), check (cmds, symtab), booleanType, false) errorType) [Tc13] [Tc21] check (astIfThen (expr, cmds), symtab) typeExpr (astLessThan (expr1, expr2), symtab) = if (eq?(typeExpr(expr, symtab), booleanType), = if (and(eq?(typeExpr(expr1,symtab),natType), check (cmds, symtab), eq?(typeExpr(expr2,symtab),natType)), false) booleanType, [Tc14] errorType) check (astIfElse (expr, cmds1, cmds2), symtab) [Tc25] = if (eq? (typeExpr (expr, symtab), booleanType), typeExpr (astNaturalConstant (m), symtab) and (check (cmds1, symtab), check (cmds2, = naturalType symtab)), [Tc26] false) typeExpr (astVariable (astIdent(name)), symtab) [Tc15] = apply (symtab, name) typeExpr (astAddition (expr1, expr2), symtab) end WrenTypeChecker = if (and(eq?(typeExpr(expr1,symtab),natType), eq?(typeExpr (expr2, symtab), natType)), naturalType, errorType)

Chapter 12 91 Chapter 12 92 The following equations perform the actual type An Interpreter for Wren checking: [Tc8] The variable in a read command has module WrenEvaluator naturalType imports Booleans, Naturals, Strings, Files, WrenValues, WrenASTs, instantiation of Mappings [Tc9] The expression in a write command has bind Entries naturalType using String for Domain using Wren-Value for Range [Tc10] The assignment target variable and using eq? for equals expression have the same type using errorString for errDomain using errorValue for errorRange rename [Tc15-18] Arithmetic operations involve using Store for Mapping expressions of naturalType using emptySto for emptyMap using updateSto for update [Tc19-24] Comparisons involve expressions of using applySto for apply naturalType. exports operations meaning ( _ , _ ) : WrenProgram, File ! File perform ( _ , _ ) : Block, File ! File elaborate ( _ , _ ) : DecSeq, Store ! Store elaborate ( _ , _ ) : Declaration, Store ! Store

Chapter 12 93 Chapter 12 94

execute ( _ , _ , _ , _ ) : CmdSeq, Store, File, File ! Store, File, File equations execute ( _ , _ , _ , _ ) : [Ev1] Cmd, Store, File, File ! Store, File, File meaning(astWrenProgram(astIdent(name),block),input) evaluate ( _ , _ ) : Relation, Store ! Boolean = perform (block, input) evaluate ( _ , _ ) : Expr, Store ! WrenValue [Ev2] end exports perform (astBlock (decs,cmds), input) = execute (cmds, variables elaborate(decs,emptySto), input, input1, input2 : File input, emptyFile) output, output1, output2 : File [Ev3] block : Block elaborate (astDecs (dec, decs), sto) decs : DecSeq = elaborate (decs,elaborate(dec, sto)) cmds, cmds1, cmds2: CmdSeq [Ev4] elaborate (astEmptyDecs, sto) cmd : Command = sto expr, expr , expr : Expr 1 2 [Ev5] sto, sto1, sto2 : Store elaborate(astDec(astIdent(name),natType), sto) value : WrenValue = updateSto(sto, name, wrenValue(0)) m,n : Natural [Ev6] name : String elaborate(astDec(astIdent(name),booleanType),sto) = updateSto(sto, name, wrenValue(false)) b : Boolean [Ev7] elaborate (astEmptyDecs, sto) = sto

Chapter 12 95 Chapter 12 96 [Ev8] [Ev14] execute(astCmds(cmd,cmds),sto1, input1, output1) execute(astWhile(expr,cmds), sto1, input1, output1) = execute (cmds, sto2, input2, output2) = if (eq? (evaluate (expr, sto1), wrenVal(true)) when = execute(astWhile(expr,cmds), sto2, input2, output2) execute (cmd, sto1, input1, output1) when = execute (cmds, sto , input , output ), [Ev9] 1 1 1 execute (astOneCmd (cmd), sto, input, output) ) = execute (cmd, sto, input, output) [Ev15] execute (astIfThen (expr, cmds), sto, input, output) [Ev10] = if (eq? (evaluate (expr, sto), wrenVal(true)) execute (astSkip, sto, input, output) execute (cmds, sto, input, output), = ) [Ev11] execute(astRead(astIdent(name)),sto,input,output) [Ev16] = if (empty? (input), execute(astIfElse(expr,cmds1,cmds2),sto,input,output) need error case here = if (eq? (evaluate (expr, sto), wrenVal(true)) ) execute (cmds1, sto, input, output) when cons(first,rest) = input execute (cmds2, sto, input, output)) [Ev12] [Ev17] execute (astWrite (expr), sto, input, output) evaluate (astAddition (expr1, expr2), sto) = when wrenValue(m) = evaluate (expr1, sto), wrenValue(n) = evaluate (expr2, sto [Ev13] execute(astAssign(astIdent(name),expr), [Ev21] sto,input,output) evaluate (astEqual (expr1, expr2), sto) = when wrenValue(m) = evaluate (expr1, sto), wrenValue(n) = evaluate (expr2, sto)

Chapter 12 97 Chapter 12 98

[Ev23] evaluate (astLessThan (expr1, expr2), sto) = wrenValue(less? (m,n)) A Wren System when wrenValue(m) = evaluate (expr1, sto), wrenValue(n) = evaluate (expr2, sto) module WrenSystem imports WrenTypeChecker, WrenEvaluator [Ev27] evaluate (astNaturalConstant (m), sto) exports = wrenValue(m) operations [Ev28] runWren : WrenProgram, File ! File evaluate (astVariable (astIdent (name)), sto) end exports = applySto (sto, name) variables end WrenEvaluator input : File program : WrenProgram equations [Ws1] runWren (program, input) = if ( check (program), eval (program, input), emptyFile) -- return an empty file if context violation, otherwise run program end WrenSystem

Chapter 12 99 Chapter 12 100 Implementing Algebraic Semantics Module Booleans boolean(true). We show the implementation of three modules: boolean(false). Booleans, Naturals, and WrenEvaluator. bnot(true, false). Expected behavior of the system: bnot(false, true). >>> Interpreting Wren via Algebraic Semantics <<< and(true, P, P). Enter name of source file: frombinary.wren and(false, true, false). program frombinary is and(false, false, false). var sum,n : integer; or(false,P,P). begin or(true,P,true) :- boolean(P). sum := 0; read n; while n<2 do xor(P, Q, R) :- or(P,Q,PorQ), and(P,Q,PandQ), sum := 2*sum+n; read n bnot(PandQ,NotPandQ), and(PorQ,NotPandQ, R). end while; write sum beq(P, Q, R) :- xor(P,Q,PxorQ), bnot(PxorQ,R). end Scan successful Module Naturals Parse successful The predicate natural succeeds with arguments Enter an input list followed by a period: of the form [1,0,1,0,1,1,2]. Output = [43] zero, succ(zero), succ(succ(zero)), …. yes Calling this predicate with a variable, such as natural(M), generates the natural numbers in this form if repeated solutions are requested by entering semicolons.

Chapter 12 101 Chapter 12 102

natural(zero). div(M, zero, R) :- natural(succ(M)) :- natural(M). write('Fatal Error: Division by zero'), The arithmetic functions follow the algebraic nl, nl, abort. specification closely. div(M, succ(N), zero) :- less(M,succ(N),true). div(M,succ(N),succ(Quotient)) :- Rather than return an error value for less(M,succ(N),false), subtraction of a larger number from a smaller sub(M,succ(N),Dividend), number or for division by zero, we print an div(Dividend,succ(N),Quotient). appropriate error message and abort the program execution. exp(M, zero, succ(zero)) :- natural(M). The comparison operations follow directly from exp(M, succ(N), R) :- exp(M,N,MexpN), their definitions. mul(M, MexpN, R). add(M, zero, M) :- natural(M). add(M, succ(N), succ(R)) :- add(M,N,R). eq(zero,zero,true). eq(zero,succ(N),false) :- natural(N). sub(zero, succ(N), R) :- eq(succ(M),zero,false) :- natural(M). write('Fatal Error: Result of subtraction is negative'), eq(succ(M),succ(N),BoolValue) :- nl, abort. eq(M,N,BoolValue). sub(M, zero, M) :- natural(M). sub(succ(M), succ(N), R) :- sub(M,N,R). less(zero,succ(N),true) :- natural(N). mul(M, zero, zero) :- natural(M). less(M,zero,false) :- natural(M). mul(M, succ(zero), M) :- natural(M). less(succ(M),succ(N),BoolValue) :- mul(M, succ(succ(N)), R) :- less(M,N,BoolValue). mul(M,succ(N),R1), add(M,R1,R). greater(M,N,BoolValue) :- less(N,M,BoolValue).

Chapter 12 103 Chapter 12 104 lesseq(M,N,BoolValue) :- less(M,N,B1), eq(M,N,B2), or(B1,B2,BoolValue). Declarations The clauses for elaborate are used to build a greatereq(M,N,BoolValue) :- store with numeric variables initialized to zero greater(M,N,B1), eq(M,N,B2), and Boolean variables initialized to false. or(B1,B2,BoolValue). elaborate([Dec|Decs],StoIn,StoOut) :- % Ev3 Two operations not specified in Naturals module. elaborate(Dec,StoIn,Sto), elaborate(Decs,Sto,StoOut). toNat converts a numeral to natural notation toNum converts a natural number to a base-ten elaborate([],Sto,Sto). % Ev4 numeral. elaborate(dec(integer,[Var]),StoIn,StoOut) :- toNat(4,Num) returns updateSto(StoIn,Var,zero,StoOut). % Ev5 Num = succ(succ(succ(succ(zero)))). elaborate(dec(boolean,[Var]),StoIn,StoOut) :- updateSto(StoIn,Var,false,StoOut). % Ev6 toNat(0,zero). toNat(Num, succ(M)) :- Num>0, NumMinus1 is Num-1, toNat(NumMinus1, M). Commands For a sequence of commands, the commands following the first command are evaluated with toNum(zero,0). the store produced by the first command toNum(succ(M),Num) :- toNum(M,Num1), Num is Num1+1. execute([Cmd|Cmds],StoIn,InputIn,OutputIn, StoOut,InputOut,OutputOut) :- % Ev8

Chapter 12 105 Chapter 12 106

execute(Cmd,StoIn,InputIn,OutputIn, Sto,Input,Output), execute(Cmds,Sto,Input,Output, The write command evaluates the expression, StoOut,InputOut,OutputOut). converts the resulting value from natural number notation to a numeric value, and execute([],Sto,Input,Output,Sto,Input,Output). appends the result to the end of the output file. % Ev9 The read command removes the first item from execute(write(Expr),Sto,Input,OutputIn, the input file, converts it to the natural number Sto,Input,OutputOut) :- % Ev2 notation, and places the result in the store. evaluate(Expr,StoIn,ExprValue), toNum(ExprValue,Value), execute(read(Var),StoIn,emptyFile,Output, mkFile(Value,ValueOut), StoOut,_,Output) :- % Ev11 concat(OutputIn,ValueOut,OutputOut). write('Fatal Error: Reading an empty file'), nl, abort. Assignment evaluates the expression using the execute(read(Var),[FirstIn|RestIn],Output, current store and then updates that store to reflect StoOut,RestIn,Output) :- % Ev11 the new binding. The skip command makes no toNat(FirstIn,Value), changes to the store or to the files. updateSto(StoIn,Var,Value,StoOut). execute(assign(Var,Expr),StoIn,Input,Output, StoOut,Input,Output) :- % Ev13 evaluate(Expr,StoIn,Value). updateSto(StoIn,Var,Value,StoOut). execute(skip,Sto,Input,Output,Sto,Input,Output). % Ev10

Chapter 12 107 Chapter 12 108 Two forms of if test Boolean expressions and let If the comparison in the while command is false, a predicate “select” perform actions. the store and files are returned unchanged. If the comparison is true, the while command is execute(if(Expr,Cmds),StoIn,InputIn,OutputIn, reevaluated with the store and files resulting from StoOut,InputOut,OutputOut) :- the execution of the while loop body. evaluate(Expr,StoIn,BoolVal), % Ev15 select(BoolVal,Cmds, [ ], execute(while(Expr,Cmds), StoIn,InputIn,OutputIn, StoIn,InputIn,OutputIn, StoOut,InputOut,OutputOut). StoOut,InputOut,OutputOut) :- evaluate(Expr,StoIn,BoolVal), % Ev14 execute(if(Expr,Cmds1,Cmds2),StoIn,InputIn, iterate(BoolVal,Expr,Cmds, OutputIn,StoOut,InputOut,OutputOut) :- StoIn,InputIn,OutputIn, evaluate(Expr,StoIn,BoolVal), % Ev16 StoOut,InputOut,OutputOut). select(BoolVal,Cmds1,Cmds2, StoIn,InputIn,OutputIn, iterate(false,Expr,Cmds, StoOut,InputOut,OutputOut). Sto,Input,Output,Sto,Input,Output). select(true,Cmds1,Cmds2, iterate(true,Expr,Cmds, StoIn,InputIn,OutputIn, StoIn,InputIn,OutputIn, StoOut,InputOut,OutputOut) :- StoOut,InputOut,OutputOut) :- execute(Cmds1,StoIn,InputIn,OutputIn, execute(Cmds,StoIn,InputIn,OutputIn, StoOut,InputOut,OutputOut). Sto,Input,Output), execute(while(Expr,Cmds), select(false,Cmds1,Cmds2, Sto,Input,Output, StoIn,InputIn,OutputIn, StoOut,InputOut,OutputOut). StoOut,InputOut,OutputOut) :- execute(Cmds2,StoIn,InputIn,OutputIn, StoOut,InputOut,OutputOut).

Chapter 12 109 Chapter 12 110

Expressions Evaluation of comparisons is similar to arithmetic The evaluation of arithmetic expressions is expressions; the equal comparison is given straightforward. below, and the five others are left as an exercise. Evaluating a variable involves looking up the evaluate(exp(equal,Expr1,Expr2),Sto,Bool) :- value in the store. evaluate(Expr1,Sto,Val1), % Ev21 evaluate(Expr2,Sto,Val2), A numeric constant is converted to natural eq(Val1,Val2,Bool). number notation and returned. evaluate(exp(plus,Expr1,Expr2),Sto,Result) :- evaluate(Expr1,Sto,Val1), % Ev17 Prolog implementation of algebraic semantics is evaluate(Expr2,Sto,Val2), similar to the denotational interpreter with respect add(Val1,Val2,Result). to command and expression evaluation. Biggest difference: evaluate(num(Constant),Sto,Value) :- Ignore native arithmetic in Prolog toNat(Constant,Value). %Ev27 Naturals module performs arithmetic based solely on a number system derived from evaluate(ide(Var),Sto,Value) :- applying a successor operation to an initial applySto(Sto,Var,Value). % Ev28 value zero.

Chapter 12 111 Chapter 12 112