-
Notifications
You must be signed in to change notification settings - Fork 3
/
Chapter14_2.hs
425 lines (282 loc) · 9.54 KB
/
Chapter14_2.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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
--------------------------------------------------------------------
--
-- Haskell: The Craft of Functional Programming, 3e
-- Simon Thompson
-- (c) Addison-Wesley, 1996-2011.
--
-- Chapter 14, part 2
-- Details of the Simulation case study in the Simulation directory.
--
--------------------------------------------------------------------
module Chapter14_2 where
import Prelude hiding (Either(..),either,Maybe(..),maybe)
import Chapter14_1 hiding (Name,NTree(..))
import Test.QuickCheck
import Control.Monad
-- Algebraic types, part 2
-- ^^^^^^^^^^^^^^^^^^^^^^^
-- Polymorphic algebraic types
-- ^^^^^^^^^^^^^^^^^^^^^^^^^^^
-- A type of pairs of elements, taken from the same type.
data Pairs a = Pr a a
-- and example elements of the type are
pair1 = Pr 2 3 :: Pairs Int
pair2 = Pr [] [3] :: Pairs [Int]
pair3 = Pr [] [] :: Pairs [a]
-- Are the two halves equal?
equalPair :: Eq a => Pairs a -> Bool
equalPair (Pr x y) = (x==y)
-- Lists
-- ^^^^^
-- Defining lists from scratch (which loses some of the special syntax for
-- lists).
infixr 5 :::
data List a = NilL | a ::: (List a)
deriving (Eq,Ord,Show,Read)
-- Binary trees
-- ^^^^^^^^^^^^
-- Binary trees carrying elements of an arbitrary type.
data Tree a = Nil | Node a (Tree a) (Tree a)
deriving (Eq,Ord,Show,Read)
-- The depth of a binary tree.
depthT :: Tree a -> Integer
depthT Nil = 0
depthT (Node n t1 t2) = 1 + max (depthT t1) (depthT t2)
-- Turning a tree into a list.
collapse :: Tree a -> [a]
collapse Nil = []
collapse (Node x t1 t2)
= collapse t1 ++ [x] ++ collapse t2
--
-- For example,
--
collapseEG
= collapse (Node 12
(Node 34 Nil Nil)
(Node 3 (Node 17 Nil Nil) Nil))
-- Mapping a function over all elements in a tree, preserving the
-- structure.
mapTree :: (a -> b) -> Tree a -> Tree b
mapTree f Nil = Nil
mapTree f (Node x t1 t2)
= Node (f x) (mapTree f t1) (mapTree f t2)
-- The union type, Either
-- ^^^^^^^^^^^^^^^^^^^^^^
-- A union type -- defined in the Prelude.
data Either a b = Left a | Right b
deriving (Eq,Ord,Read,Show)
-- Examples
eitherEG1 = Left "Duke of Prunes" :: Either String Int
eitherEG2 = Right 33312 :: Either String Int
-- In the left or the right?
isLeft :: Either a b -> Bool
isLeft (Left _) = True
isLeft (Right _) = False
-- To define a function from Either a b to c we have to deal with two cases,
either :: (a -> c) -> (b -> c) -> Either a b -> c
either f g (Left x) = f x
either f g (Right y) = g y
-- If we have a function f::a -> cand we wish to apply it to an element
-- of Either a b, there is a problem: what do we do if the element is
-- in the right-hand side of the Either type? A simple answer is to raise an error
applyLeft :: (a -> c) -> Either a b -> c
applyLeft f (Left x) = f x
applyLeft f (Right _) = error "applyLeft applied to Right"
-- Arbitrarily branching trees
data GTree a = Leaf a | Gnode [GTree a]
-- Case study: Program Errors
-- ^^^^^^^^^^^^^^^^^^^^^^^^^^
-- This section explores various ways of handling errors raised in program
-- execution.
-- \subsection*{Dummy Values}
-- \index{dummy values at errors}
-- The tail function re-defined to give an empty list when applied to the empty list.
tl :: [a] -> [a]
tl (_:xs) = xs
tl [] = []
-- Zero returned when division by zero,
divide :: Integer -> Integer -> Integer
divide n m
| (m /= 0) = n `div` m
| otherwise = 0
-- Head redefined to give a dummy value on the empty list; the value has
-- to be a parameter.
hd :: a -> [a] -> a
hd y (x:_) = x
hd y [] = y
-- Error types
-- ^^^^^^^^^^^
-- The Maybe type, as defined in the Prelude.lhs,
data Maybe a = Nothing | Just a
deriving (Eq,Ord,Read,Show)
-- An error-raising division function
errDiv :: Integer -> Integer -> Maybe Integer
errDiv n m
| (m /= 0) = Just (n `div` m)
| otherwise = Nothing
-- The function mapMaybe transmits an error value though the application of
-- the function g.
mapMaybe :: (a -> b) -> Maybe a -> Maybe b
mapMaybe g Nothing = Nothing
mapMaybe g (Just x) = Just (g x)
-- In trapping an error, we aim to return a result of type b, from an
-- input of type Maybe a; there are two cases to deal with:
-- normal result (Just); error (Nothing).
maybe :: b -> (a -> b) -> Maybe a -> b
maybe n f Nothing = n
maybe n f (Just x) = f x
-- Examples
handle1, handle2 :: Integer
handle1 = maybe 56 (1+) (mapMaybe (*3) (errDiv 9 0))
handle2 = maybe 56 (1+) (mapMaybe (*3) (errDiv 9 1))
-- Generalising the Maybe type to include an error message in the `Nothing'
-- part.
data Err a = OK a | Error String
-- Design with Algebraic Data Types
-- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-- Case study: edit distance
-- ^^^^^^^^^^^^^^^^^^^^^^^^^
-- A type to represent the different sorts of Edit operations.
data Edit = Change Char |
Copy |
Delete |
Insert Char |
Kill
deriving (Eq,Show)
-- Transforming one string into another, optimally,
transform :: String -> String -> [Edit]
transform [] [] = []
transform xs [] = [Kill]
transform [] ys = map Insert ys
transform (x:xs) (y:ys)
| x==y = Copy : transform xs ys
| otherwise = best [ Delete : transform xs (y:ys) ,
Insert y : transform (x:xs) ys ,
Change y : transform xs ys ]
--
-- How do we choose the best sequence? We choose the one with the lowest
-- cost.
best :: [[Edit]] -> [Edit]
best [x] = x
best (x:xs)
| cost x <= cost b = x
| otherwise = b
where
b = best xs
-- The cost is given by charging one for every operation except copy,
-- which is equivalent to `leave unchanged'.
cost :: [Edit] -> Int
cost = length . filter (/=Copy)
-- For testing purposes: does the best actually do the job: need to be
-- able to apply a list of edits to transform a string
edit :: [Edit] -> String -> String
edit [] string = string
edit (e:es) [] =
case e of
Insert ch -> ch : edit es []
Kill -> []
edit (e:es) string@(x:xs) =
case e of
Change ch -> ch : edit es xs
Copy -> x : edit es xs
Delete -> edit es xs
Insert ch -> ch : edit es string
Kill -> []
-- Simulation
-- ^^^^^^^^^^
-- NOTE: details of the Simulation case study are collected separately.
--
-- Algebraic types and type classes
-- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-- Movable objects
-- ^^^^^^^^^^^^^^^
data Vector = Vec Float Float
class Movable a where
move :: Vector -> a -> a
reflectX :: a -> a
reflectY :: a -> a
rotate180 :: a -> a
rotate180 = reflectX . reflectY
data Point = Point Float Float
deriving Show
instance Movable Point where
move (Vec v1 v2) (Point c1 c2) = Point (c1+v1) (c2+v2)
reflectX (Point c1 c2) = Point c1 (-c2)
reflectY (Point c1 c2) = Point (-c1) c2
rotate180 (Point c1 c2) = Point (-c1) (-c2)
data Figure = Line Point Point |
Circle Point Float
deriving Show
instance Movable Figure where
move v (Line p1 p2) = Line (move v p1) (move v p2)
move v (Circle p r) = Circle (move v p) r
reflectX (Line p1 p2) = Line (reflectX p1) (reflectX p2)
reflectX (Circle p r) = Circle (reflectX p) r
reflectY (Line p1 p2) = Line (reflectY p1) (reflectY p2)
reflectY (Circle p r) = Circle (reflectY p) r
instance Movable a => Movable [a] where
move v = map (move v)
reflectX = map reflectX
reflectY = map reflectY
-- Named objects
-- ^^^^^^^^^^^^^
-- Named objects:
class Named a where
lookName :: a -> String
giveName :: String -> a -> a
-- A named type ...
data Name a = Pair a String
-- ... as witnessed by the instance declaration.
instance Named (Name a) where
lookName (Pair obj nm) = nm
giveName nm (Pair obj _) = (Pair obj nm)
-- Putting together classes
-- ^^^^^^^^^^^^^^^^^^^^^^^^
-- See the text for details of what is going on here.
mapName :: (a -> b) -> Name a -> Name b
mapName f (Pair obj nm) = Pair (f obj) nm
instance Movable a => Movable (Name a) where
move v = mapName (move v)
reflectX = mapName reflectX
reflectY = mapName reflectY
class (Movable b, Named b) => NamedMovable b
instance Movable a => NamedMovable (Name a)
-- Reasoning about algebraic types
-- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-- The functions discussed here are all defined elsewhere.
-- QuickCheck for algebraic types
instance Arbitrary a => Arbitrary (Tree a) where
arbitrary = sized arbTree
arbTree :: Arbitrary a => Int -> Gen (Tree a)
arbTree 0 = return Nil
arbTree n
| n>0
= frequency[(1, return Nil),
(3, liftM3 Node arbitrary bush bush)]
where
bush = arbTree (div n 2)
-- collapse, map and mapTree
prop_collapse :: Eq b => (a -> b) -> Tree a -> Bool
prop_collapse f =
\t -> map f (collapse t) == collapse (mapTree f t)
-- two different ways of measuring the size of a tree
prop_sizeT :: Tree a -> Bool
prop_sizeT t =
sizeT t == (leavesT t) + length (collapse t)
-- functions used above: count the number of leaves
-- and the overall size of the tree ...
leavesT :: Tree a -> Int
leavesT Nil = 1
leavesT (Node _ t1 t2) = leavesT t1 + leavesT t2
sizeT :: Tree a -> Int
sizeT Nil = 1
sizeT (Node _ t1 t2) = 1 + sizeT t1 + sizeT t2
-- edit distance
-- does the transform actually do the right transformation?
prop_transform :: String -> String -> Property
prop_transform xs ys =
length (xs++ys) <= 15 ==> edit (transform xs ys) xs == ys
-- is it short enough?
prop_transformLength :: String -> String -> Property
prop_transformLength xs ys =
length (xs++ys) <= 15 ==> cost (transform xs ys) <= length ys + 1