Abstract
Let a domain M and a set of variables V be given. Let A := X V M X , let be the empty partial valuation (the only member of M ), and let be an object not in A. If a 2 M X , for X V , then a term t is a-closed if all variables in t are in X , an atom Pt1 tn is : a-closed if all ti are a-closed, and an identity t1 = t2 is a-closed if both of t1 ; t2 are. If a 2 M X we call X the domain of a. Use " for `unde ned' and # for `de ned'. Term interpretation in a (First Order) model M = (M; I ) wrt valuation a:
va
(ft1
:=
a(v) if v is a-closed
tn )a :=
"
I (f )ta 1
"
b with a ?!b .
0 0
Solution: Keep track of variables relevant to a particular path taken by the computation. Two distinguished registers:
dynamically free variables), l for the set of local variables that get declared and possibly assigned along a computation path (local variables are dynamically active variables).
g for the set of global variables that get assigned along a computation path (global variables are
A proper state is a triple consisting of a valuation a 2 A, a global store ga V , and a local store la V . The improper state is the item . We say that a transition (a; ga; la ) ?! (b; gb ; lb ) has the forward property if la gb dom (a), or equivalently, if la dom (a) and ga = gb . We say that b is safe for (a; ga ; la) if b = (b; gb ; lb) (i.e., b 6= ), la dom (a) and ga = gb . We say that B (PA P V P V ) f g is risky for (a; ga ; la) if B 6= ;, but no member of B is safe for (a; ga ; la). The transition rules for negation are the following: not (a; ga ; la) ?! ; and there are no b; gb; lb with (a; ga ; la ) ?! (b; gb ; lb) (a; ga ; la ) ?! (a; ga ; la)
:
there are ?! transitions for (a; ga ; la); but none of them safe for (a; ga ; la) (a; ga; la ) ?!
:
Propagation of
?!
Pt1
: t1 = t2 an a-assignment with t1 v; va ="; ta =#; v 2 la 2 : t1 =t (a; ga ; la ) ?! (a fv=tag; ga; la ) 2 : = t1 = t2 an a-assignment with t1 v; va ="; ta =#; v 2 la 2 : t1 =t (a; ga ; la) ?! (a fv=ta g; ga fvg; la) 2
a
: = t1 = t2 an a-assignment with t2 v; va ="; ta =#; v 2 la 1 :t 1= (a; ga ; la) t?! (a fv=ta g; ga fvg; la) 1
a
(a; ga ; la)
i 2 f1; 2g
times, where the value of N possibly depends on previous computation. In other words: we want to be able to perform bounded iteration, with the bound not xed at compile time, but only at run time. Assuming we have a type for nonnegative integer expressions, and N is an expression of that type, we can write this as N . Assuming that n is a natural number, we write n for: execute n times. Note the di erence between n and N caused by the fact that n is a number and N a numerical expression. The executable interpretation of n and N is given by the following transition rules:
a ?! a
a ?! +1 a ?!
n n
a ?! b b ?! +1 a ?!
n n n
a ?! b b ?! c +1 a ?! c a ?!
N
N a ="
n
a ?! N a =#; max(0; N a) = n a ?!
n N N
a ?! b N a =#; max(0; N a) = n a ?! b Executable Interpretation of Bounded Choice Suppose we want to check whether program
succeeds for any i in a bounded range of possibilities, say M::N . Again, we don't want to x the values of M and N in advance, but let them be computed. For this, we add a new construct to the language, by introducing formulas of the form v M::N , with the following transition rules:
a ?!
i
v M::N
M a ="
a ?!
v M::N
N a ="
=j ; a v?! a ?!
i v M::N
v M::N
M a = m; N a = n; m j n
=j a v?!; b M a = m; N a = n; m j n a ?! b
la dom (a) and ga = gb together guarantee the forward property for (a; ga ; la ) ?! (b; gb ; lb).
Executable interpretation is faithful to DPL in the following sense:
{ If (a; ga; la) ?! (b; gb; lb) then there are full extensions a ; b with b 2 ( )](a ). { If there are no -transitions from (a; ga; la), then for every full extension a of a it holds that ( )](a ) = ;.
0 0 0 0 0 0
i Jj t:
Destructive assignment statement x := x + 1 can be replaced by safe assignment statement x Jx x + 1. General procedure for removing the sting from assignment: 9x; x = t if x does not occur in t; (x := t) := x Jx t x =x] otherwise.
0
In dynamo , x Jy t is written as x
>> y = t.
Figure 1: Translation from dynamo to Extended DPL. (begin S1 ; : : : ; Sn end) (skip) (fail) (t1 = t2 ) (some v) (some v1 ; : : : ; vn ) (v1 >> v2 = t) (find v in N::M ] with S ) (do N times S ) (if B then S1 else S2 ) (either S1 orelse S2 ) (test B ) (donot S ) (t1 = t2 ) (Pt1 tn ) (not B ) (B1 (B1
and or
:= := := := := := := := := := := := :=
S1 ; : : : ; S n
:? ?
:= t1 = t2 := Pt1 tn := :B := B1 ; B2 := B1 B2
B2 ) B2 )
Skip Statement
skip
Fail Statement
fail
This example statement increments the value of x while using x0 as an auxiliary variable. The example statement is equivalent to the following composite statement:
begin some x0; x0 = x; some x; x = x0 + 1 end
This behaves di erent from the identity statement x=2 for input states that have no value for x. For such inputs, the identity statement x=2 will assign 2 to x, but the test statement test x = 2 will raise an exception, because the test cannot be evaluated for the current input.
If Statement Example:
if x = 2 then skip else fail
Here the condition x = 2 is read as a test. In case there is no input value for x, the example statement will raise an exception.
Note that any if statement can be paraphrased in terms of an either statement and a test statement. The if statement example above is equivalent to:
either test x = 2 orelse fail
Do Statement Example:
do 100 times begin x >> x0 = x0 + 1; y x] = 0 end
If x does have a value n for the current input, this initializes an array y n + 1]; : : : ; y n + 100]. If x does not have a value for the current input, the example raises an exception.
In its output, a distinction is made between the global variable y and the local variable x. Also note that the output indicates that the solution set is complete.
"y":0 e.g. "x":1 There are no further solutions
Its output:
"x":3 "y":2 There are no further solutions
When we change the order of the rst and the second statement the execution mechanism encounters the statement y = x + 1 in a situation where neither x nor y is known. This raises an exception which is propagated. 8
begin n = 4; m = 0; some w; w = 0; do n times { marry off the begin w >> w0 = w0 + 1; find m in 1 .. n] with begin b m] = 1; w = WifeOf m]; some b m]; b m] = 0; end end end
The output of this program is given in Figure (2). If we have full information about the preferences of the men and women we can compute the matches that are stable. When is your marriage stable? An equivalent but possibly more disturbing version of this question runs: when is your marriage in danger? If your marriage is in danger it can be (basically) for two reasons 7]: Either you yourself prefer someone else to your partner, and that other person either is single or prefers you to his/her own partner, or ... your partner prefers someone else to you, and that other person either is single or prefers your partner to his/her own partner. 9
10
A relational situation is stable if the above two dangers are avoided for everyone. How do we recognize or create stable situations? By means of a dynamo program. The program has to represent the preferences of the men and women involved. We can do that by means of two arrays m and f . f 4] 2] = 1 signi es that woman 4 puts man 2 top of her list.1
program StableMatching (WifeOf ]); begin { first we list the women's ranking of the men f f f f 1] 2] 3] 4] 1] 1] 1] 1] = = = = 4; 1; 1; 2; f f f f 1] 2] 3] 4] 2] 2] 2] 2] = = = = 2; 3; 2; 1; f f f f 1] 2] 3] 4] 3] 3] 3] 3] = = = = 3; 4; 3; 4; f f f f 1] 2] 3] 4] 4] 4] 4] 4] = = = = } 1; 2; 4; 3; } 3; 4; 4; 4;
{ second m m m m 1] 2] 3] 4] 1] 1] 1] 1] = = = =
1 Anne Kaldeway informs me that there is some world knowledge involved in devising an e cient stable marriage algorithm. One should always take the women's preferences as one's point of departure. Why? Because it is a fact of life | always according to Kaldeway | that men tend to have their preferences shaped by super cial sentiments, with virtually identical wish lists as a result.
n = 4; m = 0; do n times { record that all men are bachelors } begin m >> m0 = m0 + 1; b m] = 1 end; some w; w = 0; do n times { try to marry off the women one by one } begin w >> w0 = w0 + 1; find m in 1 .. n] with begin some i; f w] m] = i; { m is w's i-th preference } b m] = 1; { m is still a bachelor } w = WifeOf m]; { propose woman w to man m } some b m]; b m] = 0; { m is no bachelor anymore } donot find rival in 1 .. n] with begin test f w] rival] < i;{ w prevers rival to m } either b rival] = 1 { rival is still unmarried } orelse begin b rival] = 0; { or .. rival is married } test m rival] w] < m rival] WifeOf rival]]; { rival prefers w to his own Wife } end
11
The output of program StableMatching is in Figure (3). Figure 3: dynamo output for the stable marriage program
"WifeOf 4]":1 "WifeOf 1]":2 "WifeOf 2]":3 "WifeOf 3]":4 There are no further solutions
9 Example: N Queens
The challenge: position N queens on a chess-board of size N N in such manner that none of them is under attack from any of the others 7]. Represent a placement of the queens on the board as a function f from columns to rows. Then f (4) = 5 expresses that the queen of column 4 is in row 5. If f (k) = r, then: 1. No queen from i 2 f1; : : : ; k ? 1g, should be on row r. 2. No queen from i 2 f1; : : : ; k ? 1g should be on the % diagonal through r (i.e., on position r +(k ? i)). 3. No queen from i 2 f1; : : : ; k ? 1g should be on the & diagonal through r (i.e., on position r ? (k ? i)). Here is a dynamo program for the eight queens problem.
program Nqueens (f ]); begin n = 8; some k; k = 0; do n times begin k >> k0 = k0 + 1; find r in 1 .. n] with begin r = f k]; donot find i in 1 .. k-1] with either f i] = r orelse either f i] = r + (k - i) orelse f i] = r - (k - i) end end end
The program generates the 92 solutions and displays them (see Figure 4). 12
13
10 The Implementation
We now turn to the description of the Haskell implementation of dynamo . The following gives the A (almost) complete code. In fact, the paper you are now reading is the L TEX output of the literate code for dynamo . The only exception is the parsing code, which is generated by a Haskell parser generator (see below). The Haskell implementation of dynamo consists of three modules: the main module dynamo, and the modules L (for lexical scanning) and D (for parsing). Everything below that appears in a frame and is typeset in typescript font is part of the actual Haskell code.
The modules L and D contain the lexicalizer and the parser for dynamo programs.
12 Lexical Scanning
The lexical analysis maps a string (hopefully constituting a valid dynamo program) to a list of tokens. For example, the procedure lexer maps the dynamo program
begin either x = 3 orelse y = 4; x = 4 + y end
To achieve this, the lexicalizer scans the input and puts in appropriate tags. the result of lexical analysis.
Token
14
module L where data Token = TokenBegin | TokenFail | TokenFind | TokenEither | TokenThen | TokenSome | TokenName String | TokenNeg | TokenEq | TokenPlus | TokenDiv | TokenSC | TokenDot deriving Show
| | | | | | | | | | | | |
TokenEnd TokenDo TokenIn TokenOrelse TokenElse TokenDonot TokenTrue TokenAnd TokenMore TokenMinus TokenOB TokenOSB TokenComma
| | | | | | | | | | | | |
TokenSkip TokenIterations TokenWith TokenIf TokenTest TokenInt Int TokenFalse TokenOr TokenLess TokenTimes TokenCB TokenCSB TokenProgram
lexer
lexer :: String -> Token] lexer ] = ] lexer (c:cs) | isSpace c = lexer cs | isAlpha c = lexName (c:cs) | isDigit c = lexNum (c:cs) lexer ('=':cs) = TokenEq : lexer cs lexer ('+':cs) = TokenPlus : lexer cs lexer ('-':cs) = TokenMinus : lexer cs lexer ('*':cs) = TokenTimes : lexer cs lexer ('/':cs) = TokenDiv : lexer cs lexer ('(':cs) = TokenOB : lexer cs lexer (')':cs) = TokenCB : lexer cs lexer ('>':cs) = TokenMore : lexer cs lexer ('<':cs) = TokenLess : lexer cs lexer (';':cs) = TokenSC: lexer cs lexer (' ':cs) = TokenOSB: lexer cs lexer (']':cs) = TokenCSB: lexer cs lexer ('.':cs) = TokenDot: lexer cs lexer (',':cs) = TokenComma: lexer cs lexer (c:cs) | c == (chr 123) = lexComment cs 0
15
(chr 123)
is a representation of an opening curly bracket. Comments in dynamo programs start with an opening curly bracket { and end with the next matching occurrence of a closing curly bracket }. lexComment is called when an opening comment bracket is read; it puts the comment level to 0 and skips everything until another curly bracket is read. If the next curly bracket is an opening bracket, the comment level is incremented. If it is a closing bracket and we are at comment level 0 the comment ends. If it is a closing bracket at comment level greater than 0 the comment level is decremented. This allows comments to be nested. E.g., the programming fragment
do n times { record that all men are bachelors } begin m >> m0 = m0 + 1; b m] = 1 end;
This is a very handy feature when debugging dynamo programs. The representations for opening and closing curly brackets that we use are (chr 123) and (chr 125). Haskell allows to refer to `f' and `g' directly, by means of { and }. We prefer (chr 123) and (chr 125) because unlike the brackets these A representations do not interfere with the L TEX processing of the source le.2
lexComment (c:cs) n | c == (chr 125) = case n <= 0 of True -> lexer cs False -> lexComment cs (n-1) | c == (chr 123) = lexComment cs (n+1) | otherwise = lexComment cs n
lexNum
lexNum cs = TokenInt (read num) : lexer rest where (num,rest) = span isDigit cs
lexName
sorts out the identi ers from the reserved words of the language. An identi er (a name of a program or variable) is marked by putting a TokenName in front of it. Identi ers start with an alphabetic character, but may contain digits as well.
2 Recall that what you are reading now is the literate documentation of the dynamo implementation, as it was typeset A by L TEX.
16
lexName cs = case span isAlphaDigit cs of ("program",rest) -> TokenProgram : lexer rest ("begin",rest) -> TokenBegin : lexer rest ("end",rest) -> TokenEnd : lexer rest ("skip",rest) -> TokenSkip : lexer rest ("fail",rest) -> TokenFail : lexer rest ("do",rest) -> TokenDo : lexer rest ("times",rest) -> TokenIterations : lexer rest ("find", rest) -> TokenFind : lexer rest ("in", rest) -> TokenIn : lexer rest ("with", rest) -> TokenWith : lexer rest ("either",rest) -> TokenEither : lexer rest ("orelse",rest) -> TokenOrelse : lexer rest ("if",rest) -> TokenIf : lexer rest ("then",rest) -> TokenThen : lexer rest ("else",rest) -> TokenElse : lexer rest ("test",rest) -> TokenTest : lexer rest ("some",rest) -> TokenSome : lexer rest ("donot", rest) -> TokenDonot : lexer rest ("true",rest) -> TokenTrue : lexer rest ("false",rest) -> TokenFalse : lexer rest ("not",rest) -> TokenNeg : lexer rest ("and",rest) -> TokenAnd: lexer rest ("or",rest) -> TokenOr : lexer rest (name,rest) -> TokenName name : lexer rest where isAlphaDigit c = isAlpha c || isDigit c
data Var = Svar String | Ivar String Exp | Divar String Exp Exp deriving Show
17
Exp
x + (y * z)):
data Exp = Plus Exp Term | Minus Exp Term | Term Term deriving Show
Term
y * z):
data Term = Times Term Factor | Div Term Factor | Factor Factor deriving Show
Factor
data Factor = Int Int | Var Var | Brack Exp deriving Show
Bexp
x >= 0):
18
data Bexp = Bool Bool | Eq Exp Exp | Gr Exp Exp | Geq Exp Exp | Less Exp Exp | Leq Exp Exp | Neg Bexp | Conj Bexp Bexp | Disj Bexp Bexp deriving Show
Rng
1 .. 100]):
Stmt
data Stmt = Some Var | Skip | Fail | Iseq Exp Exp | Put Var Var Exp | Test Bexp | If Bexp Stmt Stmt | Either Stmt Stmt | Do Exp Stmt | Find Var Rng Stmt | Find1 Var Int Int Stmt | Not Stmt | Comp Stmts deriving Show
Stmts
19
type Stmts =
Stmt]
14 Parsing
The BNF de nition of dynamo program statements is given by the following piece of code. We give part of the input for the parser module, which is generated from the code below (typeset in typescript, between horizontal lines) by the Haskell Happy parser generator 5]. A program is either a single statement, or it starts with the reserved word program, followed by a program name, a list of display variable schemes, and a statement. The display variable schemes indicate whether a display variable is a single variable a, a variable with a single index a ], or a variable with a double index a ] ]. Indicating display variables by schemes is necessary because in dynamo nothing prevents the use of the same name a for these three purposes.
Pgm : Stmt | program Name ';' Stmt | program Name Vdecl ';' Stmt { (False, ],$1) { (False, ],$4) { (True,$3,$5) { { $2 { $3 : $1 { $1] { ] { ($1,0) { ($1,1) { ($1,2) } } } } } } } } } } }
Name : name Vdecl : '(' Vschemes ')' Vschemes : Vschemes ',' Vscheme | Vscheme | {- empty -} Vscheme : name | name ' ' ']' | name ' ' ']' ' ' ']'
20
| | | | |
do Exp times Stmt find Var in Rng with Stmt donot Stmt begin Stmts end {- empty -}
{ { { { {
The statements in a list are generated by the parser in the form of a parse stack, so the list has to be reversed to get its meaning right. The same holds for the variables is a variable list.
Stmts : Stmts ';' Stmt | Stmt Vars : Vars ',' Var | Var ',' Var { $3 : $1 { $1] { $3 : $1 { $3,$1] } } } }
Variables can be strings, strings with single indices, or strings with double indices. Note the di erence between variables and the variable schemes above.
Var : name | name ' ' Exp ']' | name ' ' Exp ']' ' ' Exp ']' { Svar $1 { Ivar $1 $3 { Divar $1 $3 $6 } } }
Rng
{ ($2,$5)
The capitalized keywords on the righthand side are the datatypes that are used for the construction of the parse trees. For example, the dynamo program
begin either x = 3 orelse y = 4; x = 4 + y end
21
(Term (Factor (Int 3)))) (Iseq (Term (Factor (Var (Svar "y")))) (Term (Factor (Int 4)))), Iseq (Term (Factor (Var (Svar "x")))) (Plus (Term (Factor (Int 4))) (Factor (Var (Svar "y"))))]
Vscheme
is the type of a variable scheme: the integer indicates the number of indexes. Pgm is the type of a program: the boolean in the rst component indicates whether a display constraint was stated, the list of variable schemes in the second component speci es the display constraint, and the statement in the third component gives the actual program. The indication of a display constraint allows us to distinguish between program Test; n = 1 and program Test (); n = 1. The rst program will display the value of n, the second will merely state that that the test evaluates to true.
A proper state is a triple consisting of a valuation, a global store and a local store. Two proper states are equal when they are equal componentwise. A state is either just a proper state, or improper.
22
data ProperState = Ps Valuation Store Store instance Eq ProperState where Ps a g l == Ps b g' l' = a == b && g == g' && l == l' type State = Maybe ProperState
The Maybe datatype is a convenient Haskell provision for handling exceptions. A datatype Maybe a for type a, has as possible values Nothing and Just a. The value Nothing signi es that an exception has arisen, the value Just a indicates that a proper answer has been computed. We will use Nothing for the improper state .
maybe_op1 :: (a -> b) -> Maybe a -> Maybe b maybe_op1 op Nothing = Nothing maybe_op1 op (Just x) = Just (op x) maybe_op2 maybe_op2 maybe_op2 maybe_op2 :: op op op (a -> a -> b) -> Maybe a -> Maybe a -> Maybe b Nothing arg = Nothing arg Nothing = Nothing (Just x) (Just y) = Just (op x y)
23
a_val_exp :: Exp -> Valuation -> Maybe Int a_val_exp(Plus a_1 a_2) s = maybe_op2 (+) (a_val_exp a_1 s) (a_val_term a_2 s) a_val_exp(Minus a_1 a_2) s = maybe_op2 (-) (a_val_exp a_1 s) (a_val_term a_2 s) a_val_exp(Term a) s = a_val_term a s a_val_term :: Term -> Valuation -> Maybe Int a_val_term(Times a_1 a_2) s = maybe_op2 (*) (a_val_term a_1 s) (a_val_factor a_2 s) a_val_term(Div a_1 a_2) s = maybe_op2 div (a_val_term a_1 s) (a_val_factor a_2 s) a_val_term(Factor a) s = a_val_factor a s
In order to handle indexed variables, we convert the index to a string, by means of the following:
intStr :: Int -> String intStr i = intStrStr i ] intStrStr :: Int -> String -> String intStrStr i s | i == last = (intToDigit last):s | otherwise = intStrStr (quot i 10) (intToDigit(last):s) where last = rem i 10
This allows us to use the index as an appendix to the variable name, and do the variable lookup:
a_val_factor :: Factor -> Valuation -> Maybe Int a_val_factor(Int n) s = Just n a_val_factor(Var (Svar x)) s = lookup x s a_val_factor(Var (Ivar x expr)) s = lookup (x ++ (' ':(intStr i)) ++ "]") s where Just i = a_val_exp expr s a_val_factor(Var (Divar x expr1 expr2)) s = lookup (x ++ ' ':(intStr i) ++ ']':' ':(intStr j) ++ "]") s where Just i = a_val_exp expr1 s Just j = a_val_exp expr2 s a_val_factor(Brack expr) s = a_val_exp expr s
b_val_exp :: Bexp -> Valuation -> Maybe Bool b_val_exp(Bool t) s = Just t b_val_exp(Eq e_1 e_2) s = maybe_op2 (==) (a_val_exp e_1 s) (a_val_exp e_2 s) b_val_exp(Gr e_1 e_2) s = maybe_op2 (>) (a_val_exp e_1 s) (a_val_exp e_2 s) b_val_exp(Geq e_1 e_2) s = maybe_op2 (>=) (a_val_exp e_1 s) (a_val_exp e_2 s) b_val_exp(Less e_1 e_2) s = maybe_op2 (<) (a_val_exp e_1 s) (a_val_exp e_2 s) b_val_exp(Leq e_1 e_2) s = maybe_op2 (<=) (a_val_exp e_1 s) (a_val_exp e_2 s) b_val_exp(Neg e) s = maybe_op1 not (b_val_exp e s) b_val_exp(Conj e_1 e_2) s = maybe_op2 (&&) (b_val_exp e_1 s) (b_val_exp e_2 s) b_val_exp(Disj e_1 e_2) s = maybe_op2 (||) (b_val_exp e_1 s) (b_val_exp e_2 s)
reset :: Variable -> Valuation -> Valuation reset v ] = ] reset v ((x,d):xds) | v == x = reset v xds | otherwise = ((x,d):reset v xds)
Taking the union of two lists. Needed further on to add elements to valuations, to stores, and to state lists. Note that no duplicates are introduced.
union :: Eq a => a] -> a] -> a] union s ] = s union s (e:ee) | elem e s = union s ee | otherwise = e:(union s ee)
Check if Exp consists of a single variable, an indexed variable or a doubly indexed variable, with the index or indices de ned for the current valuation, and if so, return it: 25
getvar :: Exp -> Valuation -> Variable] getvar (Term (Factor (Var (Svar v)))) s = v] getvar (Term (Factor (Var (Ivar v exp)))) s = make_ivar v s (a_val_exp exp s) getvar (Term (Factor (Var (Divar v exp1 exp2)))) s = make_divar v s (a_val_exp exp1 s) (a_val_exp exp2 s) getvar _ _ = ] getvarstring :: Var -> Valuation -> Variable] getvarstring (Svar x) s = x] getvarstring (Ivar x exp) s = make_ivar x s (a_val_exp exp s) getvarstring (Divar x exp1 exp2) s = make_divar x s (a_val_exp exp1 s) (a_val_exp exp2 s) getvarstring _ _ = ] make_ivar :: Variable -> Valuation -> Maybe Int -> make_ivar v val i = maybe ] f i where f j = v ++ (' ':(intStr j)) ++ "]"] Variable]
make_divar :: Variable -> Valuation -> Maybe Int -> Maybe Int -> make_divar v val i = maybe ( -> ]) f i where f i j = maybe ] g j where g j = v ++ ' ':(intStr i) ++ ']':' ':(intStr j) ++ "]"]
Variable]
processId
processes the two expressions constituting an identity, in the context of a valuation, in order to see whether the identity is an assignment or a test.
processId :: Exp -> Exp -> Valuation -> ( Variable],Maybe Int, Variable],Maybe Int) processId exp1 exp2 s = (getvar exp1 s, a_val_exp exp1 s, getvar exp2 s, a_val_exp exp2 s)
Check whether a set of outputs is safe for a given input triple (a; ga; la ). A set of outputs is safe if it contains at least one triple (b; gb ; lb ) that guarantees the forward property for (a; ga ; la) ! (b; gb ; lb). The condition for this is: la gb dom (a). Since the two stacks can only grow, and the global store grows only when a value gets assigned to a variable not in the local input store and not in the domain of the input valuation, we can replace this by two simpler conditions: gb = ga and la dom (a). Needed for a proper treatment of negation.
26
isSafe :: ProperState -> State] -> Bool isSafe _ ] = False isSafe agl states | not (localSafe agl) = False | otherwise = globalSafe agl states localSafe :: ProperState -> Bool localSafe (Ps a g l) = containedIn l
x | (x,d) <- a ]
globalSafe :: ProperState -> State] -> Bool globalSafe pstate ] = False globalSafe pstate (Nothing:ss) = globalSafe pstate ss globalSafe (Ps a g l) (Just (Ps b g' l'):ss) = g == g' || (isSafe (Ps a g l) ss) containedIn :: Eq a => a] -> a] -> Bool containedIn ] bs = True containedIn (a:as) bs = (elem a bs) && (containedIn as bs)
18 Evaluating Statements
run
runs a statement on a state, and outputs a list of states. Note that the input state may be improper. The procedure run checks whether the input is proper. If not, the output list only contains the improper state, otherwise the procedure dorun is invoked:
run :: Stmt -> State -> State] run stmt = maybe Nothing] (dorun stmt)
runs runs a sequence of statements for a list of input states, running the sequence for every member of the list of inputs and taking the union of the results.
runs :: Stmt] -> State] -> State] runs ] sts = sts runs (stmt:stmts) ] = ] runs (stmt:stmts) (st:sts) = runs stmts (union (run stmt st) (runs
stmt] sts))
doruncomp
runs a list of statements for a proper input state, and outputs a list of states. 27
doruncomp :: Stmt] -> ProperState -> State] doruncomp ] pstate = Just pstate] doruncomp stmt] pstate = dorun stmt pstate doruncomp (stmt:stmts) pstate = runs stmts (dorun stmt pstate)
doSome
is used in the evaluation of Some statements and in the evaluation of Find1 statements.
doSome :: Var -> ProperState -> ProperState doSome v (Ps a g l) = (Ps (reset x a) g (union l where x] = getvarstring v a
x]))
dorun
is the main procedure for executing single statements in the context of a proper input valuation. It takes a statement and a proper state, and returns a state list.
State]
A Some v statement resets the variable v. We isolate the procedure doSome, as we need it later on in the de nition of the evaluation of Find1 statements.
dorun (Some v) pstate = Just (doSome v pstate)]
The Skip statement returns the input. The Fail statement returns an empty state set.
Just pstate] ]
28
dorun (Iseq e1 e2) (Ps val g l) = case (processId e1 e2 val) of ( v],Nothing,_,Just m) | elem v l -> Just (Ps ((v,m):val) g l)] | otherwise -> Just (Ps ((v,m):val) (union g v]) l)] (_,Just m, v],Nothing) | elem v l -> Just (Ps ((v,m):val) g l)] | otherwise -> Just (Ps ((v,m):val) (union g v]) l)] (_,Just m,_,Just n ) | m == n -> Just (Ps val g l)] | otherwise -> ] (_,_,_,_) -> Nothing]
The Put v x exp statement merely abbreviates the sequential composition of Some x, an identity statement, Some v, and a second identity statement:
dorun (Put v x exp) pstate = doruncomp Some x,id1,Some v,id2] pstate where id1 = (Iseq (Term (Factor (Var v))) (Term (Factor (Var x)))) id2 = (Iseq (Term (Factor (Var v))) exp)
The Test statement checks the value of a Boolean expression. If the expression cannot be evaluated for the current input an exception is raised. Note the di erence between the program statements test x = 3 and x = 3. The rst raises an exception if x is not in the domain of the input, the second assigns 3 to x in that case.
dorun (Test b) (Ps val g l) | b_val_exp b val == Nothing = Nothing] | b_val_exp b val == Just True = Just (Ps val g l)] | b_val_exp b val == Just False = ]
The If statement raises an exception when its Boolean expression cannot be evaluated. Otherwise it picks the branch indicated by the evaluation of the Boolean.
dorun (If b s1 s2) (Ps val | b_val_exp b val == | b_val_exp b val == | b_val_exp b val == g l) Nothing = Nothing] Just True = dorun s1 (Ps val g l) Just False = dorun s2 (Ps val g l)
29
Evaluating the Either statement is taking the union of the results of the component statements.
dorun (Either s1 s2) (Ps val g l) = union (dorun s1 (Ps val g l)) (dorun s2 (Ps val g l))
The Do statement for bounded iteration evaluates the arithmetical expression for the number of iterations. If this fails, an exception is raised. If it succeeds, the embedded statement is executed the appropriate number of times, or skipped if the control number is not positive.
dorun (Do exp s) (Ps val g l) | a_val_exp exp val == Nothing = Nothing] | otherwise = (doruncomp (iter n s) (Ps val g l)) where Just n = (a_val_exp exp val) iter m s | m <= 0 = ] | otherwise = s:(iter (m-1) s)
The Find statement tries to evaluate the range expressions, and evaluates a Find1 statement when it nds proper values, and raises an exception otherwise.
dorun | | |
(Find var (exp1,exp2) s) (Ps val g l) a_val_exp exp1 val == Nothing = Nothing] a_val_exp exp2 val == Nothing = Nothing] otherwise = dorun (Find1 var m n s) (Ps val g l) where Just m = (a_val_exp exp1 val) Just n = (a_val_exp exp2 val)
30
dorun | | |
(Find1 var m n s) pstate n < m = ] m == n = try otherwise = union try next where try = (doruncomp id,s] new) new = (doSome var pstate) id = (Iseq (Term (Factor (Var var))) (Term (Factor (Int m)))) next = dorun (Find1 var (m+1) n s) pstate
The Not statement evaluates the embedded statement, and checks the result for safety.
dorun (Not stmt) pstate = case (dorun stmt pstate) of ] -> Just pstate] x | isSafe pstate x -> ] | otherwise -> Nothing]
])
31
splitValues :: ProperState -> (Valuation,Valuation) splitValues pstate = splitValues1 pstate ] ] splitValues1 :: ProperState -> Valuation -> Valuation -> (Valuation,Valuation) splitValues1 (Ps ] _ _) uVal eVal = (uVal,eVal) splitValues1 (Ps ((v,d):rest) g l) uVal eVal | elem v l = splitValues1 (Ps rest g l) uVal ((v,d):eVal) | otherwise = splitValues1 (Ps rest g l) ((v,d):uVal) eVal
In case there are no solutions, we say so. In case the solution has an empty set of global variables, the program was just a check, so we can return `True' as outcome. The output should distinguish between the values that are computed solutions (the universal values) and the values that are mere sample values (the existential values). The list of sample values is preceded by \e.g.".
32
showsV :: ProperState -> String -> String showsV pstate string = case (uVals,eVals) of ( ], ]) -> "True" otherwise -> showsuVal uVals (showseVal eVals string) where (uVals,eVals) = splitValues pstate showsuVal :: Valuation -> String -> String showsuVal ] string = "True" ++ string showsuVal a string = showsValues a string showseVal :: Valuation -> String -> String showseVal ] string = string showseVal a string = " e.g. " ++ (showsValues a string) showsValues :: Valuation -> String -> String showsValues ] string = ' ':string showsValues ((v,d):rest) string = shows v (':':(shows d (' ':(showsValues rest string))))
It is important to inform the user about possible incompleteness of the computed solution set. This is done by means of `There may be solutions, but none were found' if there are only improper outcomes, or `There may be further solutions' if both proper and improper outcomes are generated. showStates displays the solution set, and gives informative feedback on possible incompleteness of the computed answer. Note that (chr 10) is a representation for the \newline" character. It is possible to refer to A \newline" in Haskell directly, by means of '\n', but we refrain from doing so as this confuses the L TEX processor.
33
showStates :: State] -> String showStates ] = "False" showStates Nothing] = "There may be solutions, but none were found" showStates (Nothing:states) = showMaybeStates states showStates (Just pstate:states) = showsV pstate (showMoreStates states) showMaybeStates :: State] -> String showMaybeStates ] = "There may be solutions, but none were found" showMaybeStates Nothing] = "There may be solutions, but none were found" showMaybeStates (Nothing:states) = showMaybeStates states showMaybeStates (Just pstate:states) = showsV pstate (showMaybeMoreStates states) showMoreStates :: State] -> String showMoreStates ] = (chr 10):"There are no further solutions" showMoreStates Nothing] = (chr 10):"There may be further solutions" showMoreStates (Nothing:states) = showMaybeMoreStates states showMoreStates (Just pstate:states) = (chr 10):(showsV pstate (showMoreStates states)) showMaybeMoreStates :: State] -> String showMaybeMoreStates ] = (chr 10):"There may be further solutions" showMaybeMoreStates Nothing] = (chr 10):"There may be further solutions" showMaybeMoreStates (Nothing:states) = showMaybeMoreStates states showMaybeMoreStates (Just pstate:states) = (chr 10):(showsV pstate (showMaybeMoreStates states))
decompVar :: Variable -> Vscheme decompVar varstring = case break (x, ]) (x,(y:ys)) | ' ' `elem` ys | otherwise
(== ' ') varstring of -> (x,0) -> (x,2) -> (x,1)
varForm
varForm :: Variable -> Vscheme] -> Bool varForm variable ] = False varForm variable ((s,i):rest) = ((s,i) == (decompVar variable)) || varForm variable rest
In any case, we don't want to see all the values of the local variables. If a local variable is required for display, one example instantiation is enough. If a display constraint was speci ed, we prune away all variables from the valuations that are not speci ed by the display requirements.
pruneStates :: (Bool, Vscheme], State]) -> State] pruneStates (displayC,schemes,states) | displayC == False = pruneStates1 states ] ] | otherwise = pruneStates2 schemes states
pruneStates1 :: State] -> Valuation] -> State] -> State] pruneStates1 ] vals states = states pruneStates1 (Nothing:states) vals newstates = (Nothing:(pruneStates1 states vals newstates)) pruneStates1 (Just (Ps a g l):states) vals newstates | val `elem` vals = (pruneStates1 states vals newstates) | otherwise = (pruneStates1 states (val:vals) (Just (Ps val g l):newstates)) where val = (x,d) | (x,d) <- a, x `elem` g] pruneStates2 :: Vscheme] -> State] -> Valuation] -> State] -> State] pruneStates2 schemes ] vals states = states pruneStates2 schemes (Nothing:states) vals newstates = (Nothing:(pruneStates2 schemes states vals newstates)) pruneStates2 schemes (Just (Ps a g l):states) vals newstates | val `elem` vals = (pruneStates2 schemes states vals newstates) | otherwise = (pruneStates2 schemes states (val:vals) (Just (Ps val g l):newstates)) where val = (x,d) | (x,d) <- a, x `elem` g, varForm x schemes]
22 Wrapping it all up
Ask for a program, lexicalize and parse its contents, compute the output valuations, and prune and display the results:
35
main = do putStr "Input program: " ifile <- getLine readFile ("/home/jve/dynamo/" ++ ifile) >>= (putStr . showStates . pruneStates . compute . parse . lexer) mainw = do putStr "Input program: " ifile <- getLine s <- readFile ("/home/jve/dynamo/" ++ ifile) writeFile "dynamo.output" ((showStates . pruneStates . compute . parse . lexer) s) putStr "result written to file"
References
1] Krzysztof Apt, Jacob Brunekreef, Vincent Parrington, and Andrea Schaerf. Alma-0: An imperative language that supports declarative programming. Technical Report PNA-R9713, CWI, 1997. 2] Jan van Eijck. Programming with dynamic predicate logic. Report, CWI/ILLC, November 1998. Available from www.cwi.nl/~jve. 3] J. Groenendijk and M. Stokhof. Dynamic predicate logic. Linguistics and Philosophy, 14:39{100, 1991. 4] P. Hudak, J. Fasel, and J. Peterson. A gentle introduction to Haskell. Technical report, Yale University, 1996. Available from the Haskell homepage: http://www.haskell.org. 5] S. Marlow. Happy user guide. Description of the Haskell Parser Generator, Version 1.4, December 1997. 6] J. Peterson and K. Hammond (eds). Report on the programming language Haskell,Version 1.4. Available from the Haskell homepage: http://www.haskell.org, 1997. 7] N. Wirth. Algorithms + Datastructures = Programs. Prentice Hall, 1978.
36