Skip to content

Commit 0441b5b

Browse files
committed
Expression module reasonably far, outline of modules
1 parent 07057f3 commit 0441b5b

File tree

4 files changed

+157
-23
lines changed

4 files changed

+157
-23
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
*.ui
22
*.uo
3+
*~

Haskell/Contract/Expr.hs

+117-23
Original file line numberDiff line numberDiff line change
@@ -5,31 +5,38 @@ module Contract.Expr
55
, ExprG -- no constructors exported!
66
, BoolE, IntE, RealE
77
, i, r, b, v, pair, first, second, acc, obs, chosenBy
8-
, ppExp, certainExp, eqExp, translExp
8+
, certainExp, eqExp, translExp
99
-- evaluation. Polymorphic eval not exported
1010
, Env, emptyEnv
1111
, evalI, evalR, evalB, simplifyExp
1212
) where
1313

14-
1514
-- to define the exception
1615
import Control.Exception
1716
import Data.Typeable
17+
18+
-- for name supply
1819
import System.IO.Unsafe(unsafePerformIO)
1920
import Control.Concurrent
2021

22+
-- for pretty-printer
23+
import Text.Printf
24+
25+
2126
data Currency = EUR | DKK | SEK | USD | GBP | JPY
27+
deriving (Eq, Show, Read)
2228
-- good enough with only FX derivatives. Otherwise we could add this:
2329
-- "... | Stock String | Equity String"
2430
-- These are just tags, not used in expressions / arithmetics
2531
-- (otherwise we might want a GADT for them)
2632

27-
ppCur EUR = "EUR"
28-
ppCur DKK = "DKK"
29-
ppCur SEK = "SEK"
30-
ppCur USD = "USD"
31-
ppCur GBP = "GBP"
32-
ppCur JPY = "JPY"
33+
-- ppCur not needed, use "show"
34+
-- ppCur EUR = "EUR"
35+
-- ppCur DKK = "DKK"
36+
-- ppCur SEK = "SEK"
37+
-- ppCur USD = "USD"
38+
-- ppCur GBP = "GBP"
39+
-- ppCur JPY = "JPY"
3340

3441
-- submodule expression starts here
3542
type Var = String
@@ -58,6 +65,29 @@ data ExprG a where
5865
Equal :: Eq a => ExprG a -> ExprG a -> ExprG Bool
5966
Or :: ExprG Bool -> ExprG Bool -> ExprG Bool
6067

68+
-- | Show instance for debugging (cannot be derived automatically for GADTs)
69+
instance Show (ExprG a) where
70+
show (I n) = "I " ++ show n
71+
show (R r) = "R " ++ ppReal r
72+
show (B b) = "B " ++ show b
73+
show (V v) = "V " ++ show v
74+
show (Pair e1 e2) = "Pair " ++ par (show e1) ++ par (show e2)
75+
show (Fst e) = "Fst " ++ par (show e)
76+
show (Snd e) = "Snd " ++ par (show e)
77+
show (Obs (s,i)) = "Obs " ++ par (show s ++ "," ++ show i)
78+
show (ChosenBy (p,i)) = "ChosenBy " ++ par (show p ++ "," ++ show i)
79+
show (Acc (v,e) i a) = "Acc " ++ unwords [par (show v ++ "," ++ show e),
80+
show i, par (show a)]
81+
show (Not e) = "Not " ++ par (show e)
82+
show (Arith op e1 e2) = "Arith " ++ unwords [show op, par (show e1),
83+
par (show e2)]
84+
show (Less e1 e2) = "Less " ++ unwords [par (show e1), par (show e2)]
85+
show (Equal e1 e2) = "Equal " ++ unwords [par (show e1), par (show e2)]
86+
show (Or e1 e2) = "Or " ++ unwords [par (show e1), par (show e2)]
87+
88+
-- parenthesis around a string
89+
par s = "(" ++ s ++ ")"
90+
6191
data AOp = Plus | Minus | Times | Max | Min
6292
deriving (Show)
6393

@@ -66,8 +96,8 @@ ppOp :: AOp -> (String,Bool)
6696
ppOp Plus = ("+", True)
6797
ppOp Minus = ("-", True)
6898
ppOp Times = ("*", True)
69-
ppOp Max = ("max", False)
70-
ppOp Min = ("min", False)
99+
ppOp Max = ("max ", False)
100+
ppOp Min = ("min ", False)
71101

72102
-- reading operators
73103
instance Read AOp where
@@ -100,24 +130,26 @@ opsem Times = (*)
100130
opsem Max = max
101131
opsem Min = min
102132

103-
-- Num instance, enabling us to write "e1 + e2" for ExprG a with Num a
133+
-- | Num instance, enabling us to write "e1 + e2" for ExprG a with Num a
104134
instance (Decompose (ExprG a), Num (Content (ExprG a)), Num a) =>
105135
Num (ExprG a) where
106-
(+) = Arith Plus
107-
(*) = Arith Times
108-
(-) = Arith Minus
109-
negate = Arith Minus (fromInteger 0)
136+
(+) = arith Plus
137+
(*) = arith Times
138+
(-) = arith Minus
139+
negate = arith Minus (fromInteger 0)
110140
abs a = (constr a) (abs (content a))
111141
signum a = (constr a) (signum (content a))
112142
fromInteger n = (constr (undefined :: ExprG a)) (fromInteger n)
113143
-- there's a pattern... f a = (constr a) (f (content a))
114144

115-
-- enabled with this - slightly weird - helper class
145+
-- | Num instances are possible through this - slightly weird - helper
146+
-- class which extracts constructors and values
116147
class Num a => Decompose a where
117148
type Content a
118149
constr :: a -> (Content a -> a)
119150
content :: Num (Content a) => a -> Content a
120151

152+
-- NB do we _ever_ use Int expressions? Maybe dump this whole weird thing
121153
instance Decompose (ExprG Int) where
122154
type Content (ExprG Int) = Int
123155
constr _ = I
@@ -128,6 +160,8 @@ instance Decompose (ExprG Double) where
128160
constr _ = R
129161
content x = evalR emptyEnv x
130162

163+
-- the smart constructors of the interface (here: simple)
164+
131165
i = I -- :: Int -> IntE
132166
r = R -- :: Double -> RealE
133167
b = B -- :: Bool -> BoolE
@@ -137,12 +171,23 @@ first = Fst
137171
second = Snd
138172
obs = Obs
139173
chosenBy = ChosenBy
174+
(!<!) :: Ord a => ExprG a -> ExprG a -> ExprG Bool
175+
(!<!) = Less
176+
(!=!) :: Eq a => ExprG a -> ExprG a -> ExprG Bool
177+
(!=!) = Equal
178+
179+
infixl 4 !<!
180+
infixl 4 !=!
181+
182+
-- +, -, * come from the Num instance
183+
maxx,minn :: Num a => ExprG a -> ExprG a -> ExprG a
184+
maxx = Arith Max -- instance magic would require an Ord instance...
185+
minn = Arith Min -- ...which requires an Eq instance
140186

141187
acc :: (Num a) => (ExprG a -> ExprG a) -> Int -> ExprG a -> ExprG a
142188
acc _ 0 a = a
143189
acc f i a = let v = newName "v"
144190
in Acc (v,f (V v)) i a
145-
146191
-- using a unique supply, the quick way...
147192
{-# NOINLINE idSupply #-}
148193
idSupply :: MVar Int
@@ -152,16 +197,16 @@ newName s = unsafePerformIO (do next <- takeMVar idSupply
152197
putMVar idSupply (next+1)
153198
return (s ++ show next))
154199

155-
-- equality: comparing syntax by hash, considering commutativity
200+
201+
-- | expression equality, comparing their syntax by hash
156202
eqExp :: ExprG a -> ExprG a -> Bool
157203
eqExp e1 e2 = hashExp e1 == hashExp e2
158204

205+
-- | Compute a hash of an expression, for syntactic comparisons. Considers commutativity by symmetric hashing scheme for commutative operations.
159206
hashExp :: ExprG a -> Integer
160-
hashExp e = error "not defined yet"
161-
162-
ppExp :: ExprG a -> String
163-
ppExp e = error "not defined yet"
207+
hashExp e = error "must be copied from SML code"
164208

209+
-- | Does an expression contain any observables or choices?
165210
certainExp :: ExprG a -> Bool
166211
certainExp e = case e of
167212
V _ -> False -- if variables are used only for functions in Acc, we could return true here!
@@ -180,6 +225,7 @@ certainExp e = case e of
180225
Equal e1 e2 -> certainExp e1 && certainExp e2
181226
Or e1 e2 -> certainExp e1 && certainExp e2
182227

228+
-- | translating an expression in time
183229
translExp :: ExprG a -> Int -> ExprG a
184230
translExp e 0 = e
185231
translExp e d =
@@ -200,8 +246,55 @@ translExp e d =
200246
Equal e1 e2 -> Equal (translExp e1 d) (translExp e2 d)
201247
Or e1 e2 -> Or (translExp e1 d) (translExp e2 d)
202248

249+
-----------------------------------------------------------------
250+
-- Pretty-print an expression (not the same as the Show instance)
251+
252+
-- | internal: convert daycount to years/months/days, using 30/360 convention
253+
ppTime :: Int -> String
254+
ppTime 0 = "0d"
255+
ppTime t = if null s then "0d" else s
256+
where years = t `div` 360
257+
months = (t `div` 30) `mod` 12 -- (t mod 360) div 30
258+
days = t `mod` 30
259+
str n c = if n == 0 then "" else show n ++ c:[]
260+
s = concat (zipWith str [years,months,days] "ymd")
261+
262+
-- | real numbers printed with four decimal places (FX fashion)
263+
ppReal :: Double -> String
264+
ppReal = printf "%.4f"
265+
266+
-- | internal: print an expression, using an int printing function
267+
ppExp0 :: (Int -> String) -> ExprG a -> String
268+
ppExp0 ppTime e =
269+
case e of
270+
V s -> s
271+
I i -> ppTime i
272+
R r -> ppReal r
273+
B b -> show b
274+
Pair e1 e2 -> par (ppExp0 ppTime e1 ++ "," ++ ppExp0 ppTime e2)
275+
Fst e -> "first" ++ par (ppExp0 ppTime e)
276+
Snd e -> "second" ++ par (ppExp0 ppTime e)
277+
Acc f i e -> "acc" ++ par(ppFun f ++ "," ++ show i ++ "," ++ ppExp e)
278+
Obs (s,off) -> "Obs" ++ par (s ++ "@" ++ ppTime off)
279+
ChosenBy (p,i) -> "Chosen by " ++ p ++ " @ " ++ ppTime i
280+
Not e1 -> "not" ++ par (ppExp e1)
281+
Arith op e1 e2 -> let (c,infx) = ppOp op
282+
in if infx then par(ppExp e1 ++ c ++ ppExp e2)
283+
else c ++ par (ppExp e1) ++ ' ':par(ppExp e2)
284+
Less e1 e2 -> par(ppExp0 ppTime e1 ++ " < " ++ ppExp0 ppTime e2)
285+
Equal e1 e2 -> par(ppExp0 ppTime e1 ++ "==" ++ ppExp0 ppTime e2)
286+
Or e1 e2 -> par(ppExp e1 ++ "||" ++ ppExp e2)
287+
where ppExp e = ppExp0 ppTime e
288+
ppFun (v,e) = "\\" ++ v ++ " -> " ++ ppExp e
289+
290+
-- | pretty-printing an expression, using normal printer for Int
291+
ppExp = ppExp0 show
292+
-- | pretty-printing an expression, using time printer for Int
293+
ppTimeExp = ppExp0 ppTime
294+
203295
--------------------------------------------------------------
204-
-- Evaluation:
296+
-- Evaluation of expressions:
297+
205298
data EvalExc = Eval String deriving (Read,Show,Typeable)
206299
instance Exception EvalExc
207300

@@ -264,5 +357,6 @@ eval env e =
264357
(_, B True ) -> B True
265358
(bb1, bb2) -> Or bb1 bb2
266359

360+
-- | simplify an expression, using an environment
267361
simplifyExp env e = eval env e
268362

Haskell/Contract/Type.hs

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
module Contract.Type
2+
( Contract -- no constructors exported
3+
, Days, Party
4+
, transl -- smart constructors instead
5+
) where
6+
7+
import Contract.Expr
8+
9+
type Days = Int
10+
type Party = String
11+
12+
data Contract = Zero
13+
| Transfer Party Party Currency
14+
| Transl Days Contract
15+
| Both Contract Contract
16+
-- ...
17+
deriving (Eq,Show)
18+
19+
transl :: Int -> Contract -> Contract
20+
transl d c | d < 0 = error "transl: negative time"
21+
transl 0 c = c
22+
transl _ Zero = Zero
23+
transl d (Transl d' c) = Transl (d+d') c
24+
transl d c = Transl d c
25+

Haskell/modules.txt

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
Module structure
2+
----------------
3+
Contract
4+
Contract.Expr -- expression types, p.printer, evaluation
5+
6+
Contract.Type -- contract type and p.printer
7+
8+
Contract.Date -- date library
9+
10+
Contract.Instrument -- canned FX product functions
11+
12+
Contract.Transform -- simplification/evaluation, normal form
13+
14+
Contract.Analysis -- trigger extraction etc (?)

0 commit comments

Comments
 (0)