-
Notifications
You must be signed in to change notification settings - Fork 4
/
GenericMonads.hs
151 lines (109 loc) · 4.06 KB
/
GenericMonads.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
{-
---
fulltitle: "In class exercise: General Monadic Functions"
date: October 23, 2024
---
-}
module GenericMonads where
import Data.Char qualified as Char
import Test.HUnit
import Prelude hiding (mapM, sequence)
{-
Generic Monad Operations
========================
This problem asks you to recreate some of the operations in the
[Control.Monad](https://hackage.haskell.org/package/base-4.14.2.0/docs/Control-Monad.html)
library. You should *not* use any of the functions defined in that library to
solve this problem. (These functions also appear in more general forms
elsewhere, so other libraries that are off limits for this problem include
`Control.Applicative`, `Data.Traversable` and `Data.Foldable`.)
NOTE: because these operations are so generic, the types will really help you
figure out the implementation, even if you don't quite know what the function should do.
For that reason you should also test *each* of these functions with at least two unit test
cases, one using the `Maybe` monad, and one using the `List` monad. After you
you have tried the function out, try to describe in words what each operation does
for that specific monad.
Here is the first one as an example.
-}
-- (a)
{-
Given the type signature:
-}
mapM :: (Monad m) => (a -> m b) -> [a] -> m [b]
{-
We implement it by recursion on the list argument.
-}
mapM _ [] = return []
mapM f (x : xs) = do
b <- f x
bs <- mapM f xs
return (b : bs)
{-
Then define the following test cases, which make use of the following
helper functions.
-}
maybeUpper :: Char -> Maybe Char
maybeUpper x = if Char.isAlpha x then Just (Char.toUpper x) else Nothing
onlyUpper :: [Char] -> [Char]
onlyUpper = filter Char.isUpper
-- >>> mapM maybeUpper "sjkdhf"
-- Just "SJKDHF"
-- >>> mapM maybeUpper "sa2ljsd"
-- Nothing
-- >>> mapM onlyUpper ["QuickCheck", "Haskell"]
-- ["QH","CH"]
-- >>> mapM onlyUpper ["QuickCheck", ""]
-- []
{-
Finally, we observe that this function is a generalization of List.map, where
the mapped function can return its value in some monad m.
-}
-- (b)
foldM :: (Monad m) => (a -> b -> m a) -> a -> [b] -> m a
foldM = error "foldM: unimplemented"
-- (c)
sequence :: (Monad m) => [m a] -> m [a]
sequence = error "sequence: unimplemented"
-- (d) This one is the Kleisli "fish operator"
--
(>=>) :: (Monad m) => (a -> m b) -> (b -> m c) -> a -> m c
(>=>) = error ">=>: unimplemented"
-- (e)
join :: (Monad m) => m (m a) -> m a
join = error "join: unimplemented"
-- (f) Define the 'liftM' function
liftM :: (Monad m) => (a -> b) -> m a -> m b
liftM = error "liftM: unimplemented"
-- Thought question: Is the type of `liftM` similar to that of another
-- function we've discussed recently?
-- (g) And its two-argument version ...
liftM2 :: (Monad m) => (a -> b -> r) -> m a -> m b -> m r
liftM2 = error "liftM2: unimplemented"
{-
-------------------------------------------------------------------------
General Applicative Functions
=============================
Which of these functions above can you equivalently rewrite using `Applicative`?
i.e. for which of the definitions below, can you replace `undefined` with
a definition that *only* uses members of the `Applicative` (and `Functor`)
type class. (Again, do not use functions from `Control.Applicative`,
`Data.Foldable` or `Data.Traversable` in your solution.)
If you provide a definition, you should write test cases that demonstrate that it
has the same behavior on `List` and `Maybe` as the monadic versions above.
-}
-- NOTE: you will not be able to define all of these, but be sure to test the
-- ones that you do
mapA :: (Applicative f) => (a -> f b) -> [a] -> f [b]
mapA = undefined
foldA :: forall f a b. (Applicative f) => (a -> b -> f a) -> a -> [b] -> f a
foldA = undefined
sequenceA :: (Applicative f) => [f a] -> f [a]
sequenceA = undefined
kleisliA :: (Applicative f) => (a -> f b) -> (b -> f c) -> a -> f c
kleisliA = undefined
joinA :: (Applicative f) => f (f a) -> f a
joinA = undefined
liftA :: (Applicative f) => (a -> b) -> f a -> f b
liftA f x = undefined
liftA2 :: (Applicative f) => (a -> b -> r) -> f a -> f b -> f r
liftA2 f x y = undefined