-
Notifications
You must be signed in to change notification settings - Fork 23
/
Copy pathlab12-writer.hs
138 lines (112 loc) · 3.36 KB
/
lab12-writer.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
import Control.Monad
import Data.Maybe
--- Monada StringWriter
newtype StringWriter a = StringWriter { runStringWriter :: (a, String) }
-- Afișează valoarea conținută în monadă
instance Show a => Show (StringWriter a) where
show x =
let (value, message) = runStringWriter x
in "Output: " ++ message ++ "Value: " ++ show value
instance Monad StringWriter where
-- Creez o nouă valoare din monadă folosind direct constructorul
return value = StringWriter (value, "")
-- Aplic funcția și concatenez output-urile
(StringWriter (value, message)) >>= f =
let (newValue, newMessage) = runStringWriter $ f value
in StringWriter (newValue, message ++ newMessage)
-- În versiunile mai noi de Haskell trebuie să definesc și instanțe ale acestor clase:
instance Functor StringWriter where
fmap = liftM
instance Applicative StringWriter where
pure = return
(<*>) = ap
--- Limbajul și Interpretorul
-- Prescurtare pentru monada folosită
type M = StringWriter
showM :: Show a => M a -> String
showM = show
tell :: String -> StringWriter ()
tell message = StringWriter ((), message)
type Name = String
data Term
= Var Name
| Con Integer
| Out Term
| Term :+: Term
| Lam Name Term
| App Term Term
deriving (Show)
data Value
= Num Integer
| Fun (Value -> M Value)
| Wrong
instance Show Value where
show (Num x) = show x
show (Fun _) = "<function>"
show Wrong = "<wrong>"
-- Calculează rezultatul adunării a două valori.
-- Funcționează doar pentru numere.
add :: Value -> Value -> M Value
add (Num a) (Num b) = return $ Num (a + b)
add _ _ = return Wrong
-- Calculează rezultatul aplicării unei funcții la o valoare.
-- Primul parametru trebuie să fie funcție.
apply :: Value -> Value -> M Value
apply (Fun f) v = f v
apply _ _ = return Wrong
type Environment = [(Name, Value)]
interp :: Term -> Environment -> M Value
-- Încercăm să returnăm valoarea pentru variabila dată.
-- Dacă nu există, returnăm `Wrong`
interp (Var name) env = return $ fromMaybe Wrong (lookup name env)
-- Returnăm valoarea pentru constanta dată
interp (Con i) _ = return $ Num i
interp (Out t) env = do
-- Evaluez termenul
v <- interp t env
-- Îi afișez valoare
tell $ show v ++ "; "
-- Returnez
return v
-- Evaluăm suma a doi termeni
interp (t1 :+: t2) env = do
-- Evaluăm cei doi termeni
v1 <- interp t1 env
v2 <- interp t2 env
-- Calculăm suma valorilor lor
add v1 v2
interp (Lam paramName t) env = do
-- Definim o nouă funcție, care evaluează termenul într-un environment
-- în care avem o valoare dată pentru parametru
let lambdaFunc = \x -> interp t ((paramName, x) : env)
return $ Fun lambdaFunc
-- Aplicăm cei doi termeni
interp (App t1 t2) env = do
-- Le evaluăm valorile
v1 <- interp t1 env
v2 <- interp t2 env
-- Aplicăm primul termen cu al doilea
apply v1 v2
test :: Term -> String
test t = showM $ interp t []
-- Ar trebui să afișeze 7
pgm :: Term
pgm =
App (Lam "y" (
App (App
(Lam "f" (Lam "y" (App (Var "f") (Var "y"))))
(Lam "x" (Var "x" :+: Var "y")))
(Con 3))
) (Con 4)
-- Ar trebui să afișeze 42
pgm1 :: Term
pgm1 =
App
(Lam "x" ((Var "x") :+: (Var "x")))
((Con 10) :+: (Con 11))
-- Ar trebui să afișeze "Output: 41; 1; Value: 42"
pgm2 :: Term
pgm2 = Out (Con 41) :+: Out (Con 1)
-- test pgm
-- test pgm1
-- test pgm2