@@ -17,7 +17,7 @@ import Text.Read (readMaybe)
17
17
-- sumTwoMaybes Nothing Nothing ==> Nothing
18
18
19
19
sumTwoMaybes :: Maybe Int -> Maybe Int -> Maybe Int
20
- sumTwoMaybes = todo
20
+ sumTwoMaybes = liftA2 (+)
21
21
22
22
------------------------------------------------------------------------------
23
23
-- Ex 2: Given two lists of words, xs and ys, generate all statements
@@ -36,7 +36,7 @@ sumTwoMaybes = todo
36
36
-- "code is not suffering","code is not life"]
37
37
38
38
statements :: [String ] -> [String ] -> [String ]
39
- statements = todo
39
+ statements xs ys = liftA2 ( \ x y -> [x ++ " is " ++ y, x ++ " is not " ++ y]) xs ys >>= id
40
40
41
41
------------------------------------------------------------------------------
42
42
-- Ex 3: A simple calculator with error handling. Given an operation
@@ -54,7 +54,7 @@ statements = todo
54
54
-- calculator "double" "7x" ==> Nothing
55
55
56
56
calculator :: String -> String -> Maybe Int
57
- calculator = todo
57
+ calculator op x = liftA2 ($) ( lookup op [( " negate " , negate ), ( " double " , ( * 2 ))]) (readMaybe x)
58
58
59
59
------------------------------------------------------------------------------
60
60
-- Ex 4: Safe division. Implement the function validateDiv that
@@ -71,7 +71,7 @@ calculator = todo
71
71
-- validateDiv 0 3 ==> Ok 0
72
72
73
73
validateDiv :: Int -> Int -> Validation Int
74
- validateDiv = todo
74
+ validateDiv x y = check (y /= 0 ) " Division by zero! " (x `div` y)
75
75
76
76
------------------------------------------------------------------------------
77
77
-- Ex 5: Validating street addresses. A street address consists of a
@@ -101,7 +101,10 @@ data Address = Address String String String
101
101
deriving (Show ,Eq )
102
102
103
103
validateAddress :: String -> String -> String -> Validation Address
104
- validateAddress streetName streetNumber postCode = todo
104
+ validateAddress streetName streetNumber postCode =
105
+ Address <$> check (length streetName <= 20 ) " Invalid street name" streetName
106
+ <*> check (all isDigit streetNumber) " Invalid street number" streetNumber
107
+ <*> check (length postCode == 5 && all isDigit postCode) " Invalid postcode" postCode
105
108
106
109
------------------------------------------------------------------------------
107
110
-- Ex 6: Given the names, ages and employment statuses of two
@@ -123,7 +126,10 @@ data Person = Person String Int Bool
123
126
twoPersons :: Applicative f =>
124
127
f String -> f Int -> f Bool -> f String -> f Int -> f Bool
125
128
-> f [Person ]
126
- twoPersons name1 age1 employed1 name2 age2 employed2 = todo
129
+ twoPersons name1 age1 employed1 name2 age2 employed2 =
130
+ liftA2 (\ p1 p2 -> [p1, p2])
131
+ (Person <$> name1 <*> age1 <*> employed1)
132
+ (Person <$> name2 <*> age2 <*> employed2)
127
133
128
134
------------------------------------------------------------------------------
129
135
-- Ex 7: Validate a String that's either a Bool or an Int. The return
@@ -143,7 +149,14 @@ twoPersons name1 age1 employed1 name2 age2 employed2 = todo
143
149
-- boolOrInt "Falseb" ==> Errors ["Not a Bool","Not an Int"]
144
150
145
151
boolOrInt :: String -> Validation (Either Bool Int )
146
- boolOrInt = todo
152
+ boolOrInt s =
153
+ (Left <$> checkBool s) <|> (Right <$> checkInt s)
154
+ where
155
+ checkBool str = check (str == " True" || str == " False" ) " Not a Bool" (read str :: Bool )
156
+ checkInt str = check (isJust (readMaybe str :: Maybe Int )) " Not an Int" (fromJust (readMaybe str :: Maybe Int ))
157
+ isJust (Just _) = True
158
+ isJust Nothing = False
159
+ fromJust (Just x) = x
147
160
148
161
------------------------------------------------------------------------------
149
162
-- Ex 8: Improved phone number validation. Implement the function
@@ -167,7 +180,10 @@ boolOrInt = todo
167
180
-- ==> Errors ["Too long"]
168
181
169
182
normalizePhone :: String -> Validation String
170
- normalizePhone = todo
183
+ normalizePhone s =
184
+ let stripped = filter (not . isSpace) s
185
+ in check (length stripped <= 10 ) " Too long" stripped
186
+ *> traverse (\ c -> check (isDigit c) (" Invalid character: " ++ [c]) c) stripped
171
187
172
188
------------------------------------------------------------------------------
173
189
-- Ex 9: Parsing expressions. The Expression type describes an
@@ -211,7 +227,33 @@ data Expression = Plus Arg Arg | Minus Arg Arg
211
227
deriving (Show , Eq )
212
228
213
229
parseExpression :: String -> Validation Expression
214
- parseExpression = todo
230
+ parseExpression s =
231
+ case words s of
232
+ [s1, op, s2] ->
233
+ let opV = parseOp op
234
+ arg1V = parseArg s1
235
+ arg2V = parseArg s2
236
+ in opV <*> arg1V <*> arg2V
237
+ _ -> invalid (" Invalid expression: " ++ s)
238
+
239
+ parseOp :: String -> Validation (Arg -> Arg -> Expression )
240
+ parseOp op =
241
+ case op of
242
+ " +" -> pure Plus
243
+ " -" -> pure Minus
244
+ _ -> invalid (" Unknown operator: " ++ op)
245
+
246
+ parseArg :: String -> Validation Arg
247
+ parseArg s =
248
+ let n = readMaybe s :: Maybe Int
249
+ v = maybe (invalid (" Invalid number: " ++ s)) (pure . Number ) n
250
+ isVar = length s == 1 && isAlpha (head s)
251
+ var = if isVar then pure (Variable (head s)) else invalid (" Invalid variable: " ++ s)
252
+ in case (n, isVar) of
253
+ (Just n, _) -> pure (Number n)
254
+ (_, True ) -> pure (Variable (head s))
255
+ _ -> v *> var
256
+
215
257
216
258
------------------------------------------------------------------------------
217
259
-- Ex 10: The Priced T type tracks a value of type T, and a price
@@ -236,11 +278,11 @@ data Priced a = Priced Int a
236
278
deriving (Show , Eq )
237
279
238
280
instance Functor Priced where
239
- fmap = todo
281
+ fmap f ( Priced p x) = Priced p (f x)
240
282
241
283
instance Applicative Priced where
242
- pure = todo
243
- liftA2 = todo
284
+ pure = Priced 0
285
+ liftA2 f ( Priced p1 x1) ( Priced p2 x2) = Priced (p1 + p2) (f x1 x2)
244
286
245
287
------------------------------------------------------------------------------
246
288
-- Ex 11: This and the next exercise will use a copy of the
@@ -273,7 +315,7 @@ instance MyApplicative [] where
273
315
myLiftA2 = liftA2
274
316
275
317
(<#>) :: MyApplicative f => f (a -> b ) -> f a -> f b
276
- f <#> x = todo
318
+ f <#> x = myLiftA2 ($) f x
277
319
278
320
------------------------------------------------------------------------------
279
321
-- Ex 12: Reimplement fmap using liftA2 and pure. In practical terms,
@@ -290,7 +332,7 @@ f <#> x = todo
290
332
-- myFmap negate [1,2,3] ==> [-1,-2,-3]
291
333
292
334
myFmap :: MyApplicative f => (a -> b ) -> f a -> f b
293
- myFmap = todo
335
+ myFmap f x = myPure f <#> x
294
336
295
337
------------------------------------------------------------------------------
296
338
-- Ex 13: Given a function that returns an Alternative value, and a
@@ -317,7 +359,7 @@ myFmap = todo
317
359
-- ==> Errors ["zero","zero","zero"]
318
360
319
361
tryAll :: Alternative f => (a -> f b ) -> [a ] -> f b
320
- tryAll = todo
362
+ tryAll f = foldr ( (<|>) . f) empty
321
363
322
364
------------------------------------------------------------------------------
323
365
-- Ex 14: Here's the type `Both` that expresses the composition of
@@ -342,7 +384,7 @@ newtype Both f g a = Both (f (g a))
342
384
deriving Show
343
385
344
386
instance (Functor f , Functor g ) => Functor (Both f g ) where
345
- fmap = todo
387
+ fmap f ( Both x) = Both ( fmap ( fmap f) x)
346
388
347
389
------------------------------------------------------------------------------
348
390
-- Ex 15: The composition of two Applicatives is also an Applicative!
@@ -370,5 +412,5 @@ instance (Functor f, Functor g) => Functor (Both f g) where
370
412
-- Errors ["fail 1","fail 2"]]
371
413
372
414
instance (Applicative f , Applicative g ) => Applicative (Both f g ) where
373
- pure = todo
374
- liftA2 = todo
415
+ pure x = Both ( pure ( pure x))
416
+ liftA2 f ( Both x) ( Both y) = Both (liftA2 (liftA2 f) x y)
0 commit comments