-
Notifications
You must be signed in to change notification settings - Fork 0
/
36monadsDoNotation.hs
313 lines (222 loc) · 9.23 KB
/
36monadsDoNotation.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
import qualified Data.Map as Map
-- do notation
-- revision,
-- to create a helloName IO action
-- that asks the user for their name and then says hello to them.
askForName :: IO ()
askForName = putStrLn "What is your name?"
nameStatement :: String -> String
nameStatement name = "Hello, " ++ name ++ "!"
-- harder to read:
nameStatementIO :: IO String
nameStatementIO = (>>=) (askForName >> getLine) customLam
where customLam = (\name -> return (nameStatement name) )
-- the book version
nameStatementIO2 :: IO ()
nameStatementIO2 = askForName >> getLine >>= (\ n -> return (nameStatement n)) >>= putStrLn
{-
Write a program by using the tools of the Monad type class
that takes a pair of values in a context,
and then returns the maximum of each pair. Here’s your type signature to get you started:
maxPairM :: (Monad m, Ord a) => m (a,a) -> m a
-}
maxTup x = max (fst x) (snd x)
constructTup v1 v2 = (v1, v2)
tupIO :: (Int, Int) -> IO (Int,Int)
tupIO val = pure val
tupIO2 :: IO (Int,Int)
tupIO2 = (fmap constructTup getLineAsInt) <*> getLineAsInt
where getLineAsInt = read <$> getLine
maxPairM valsInContext = valsInContext >>= (\ x -> return (maxTup x) )
maxPairM2 :: IO Int
maxPairM2 = tupIO2 >>= (\ x -> return (maxTup x) )
maxPairM2' :: IO ()
maxPairM2' = (fmap show maxPairM2) >>= putStrLn
-- random experimentation over
------------------------------------------------------
---------------------- DO ----------------------------
-- We will rewrite the above via DO notation
-- nameStatementIO2 :: IO ()
-- nameStatementIO2 = askForName >> getLine >>= (\ n -> return (nameStatement n)) >>= putStrLn
nameStatementIO3 :: IO ()
nameStatementIO3 = do
askForName
x <- getLine
putStrLn (nameStatement x)
nameStatementIO4 :: IO ()
nameStatementIO4 = askForName >>
getLine >>=
( \ n -> return (nameStatement n)) >>=
putStrLn
-----------------------
-----------------------
experiment y = let x = 4
in x + y
experiment2 y = (\ x -> x + y) 4
----------------------
-- Rewrite echo by using do-notation.
echo :: IO ()
echo = do
x <- getLine
putStrLn (x)
-----------------------------------
------ code reuse using monads ---
data Grade = F | D | C | B | A deriving (Eq, Ord, Enum, Show, Read)
data Degree = HS | BA | MS | PhD deriving (Eq, Ord, Enum, Show, Read)
data Candidate = Candidate
{ candidateId :: Int
, codeReview :: Grade
, cultureFit :: Grade
, education :: Degree } deriving Show
viable :: Candidate -> Bool
viable candidate = all (== True) tests
where passedCoding = codeReview candidate > B
passedCultureFit = cultureFit candidate > C
educationMin = education candidate >= MS
tests = [passedCoding
,passedCultureFit
,educationMin]
adityaVerma = Candidate { candidateId = 1, codeReview = A, cultureFit = B, education = BA }
isAdityaVermaViable = viable adityaVerma
readInt :: IO Int
readInt = getLine >>= (return . read)
-- read just converts, say, String -> Int, return puts the wrapper on it.
-- or
readInt3 :: IO Int
readInt3 = read <$> getLine
-- how are these different? esp the first two
readGrade :: IO Grade
readGrade = getLine >>= (return . read)
-- revision: getLine returns an IO String this gets piped >>= to (return . read ) which takes String -> IO String
readDegree :: IO Degree
readDegree = getLine >>= (return . read)
{-With these helper actions, you can create a single IO action that reads in a candidate.-}
readCandidate2 :: IO ()
readCandidate2 = do
putStrLn "Enter Grade for code review"
grade <- readGrade
putStrLn "Enter Grade for culture fit"
cultureFit <- readGrade
putStrLn "Enter Education BA/ MS / Phd"
edu <- readDegree
putStrLn "Enter id"
id <- readInt
let candidate = Candidate { candidateId = id, codeReview = grade, cultureFit = cultureFit, education = edu}
let viability = viable candidate
putStrLn ( show viability)
-- the above is bad because it is not modularized,
-- to make it more moduralized, split responsibility
readCandidate :: IO Candidate
readCandidate = do
putStrLn "enter id:"
cId <- readInt
putStrLn "enter code grade:"
codeGrade <- readGrade
putStrLn "enter culture fit grade:"
cultureGrade <- readGrade
putStrLn "enter education:"
degree <- readDegree
return (Candidate { candidateId = cId
, codeReview = codeGrade
, cultureFit = cultureGrade
, education = degree })
assessCandidate :: IO String
assessCandidate = do
candidate <- readCandidate
let passed = viable candidate
if passed then return "passed"
else return "failed"
-- Rewrite readGrade with do-notation.
readGrade2 :: IO Grade
readGrade2 = do
grade <- getLine
return (read grade)
-- Revision: without do notation:
readGrade3 :: IO Grade
readGrade3 = putStrLn "enter grade " >> getLine >>= ( return . read )
--- talking about code reuse, let us do some computations,
-- if the candidate is wrapped in a Maybe
-- Step 1 Creating a Data.Map
candidate1 :: Candidate
candidate1 = Candidate { candidateId = 1
, codeReview = A
, cultureFit = A
, education = BA }
candidate2 :: Candidate
candidate2 = Candidate { candidateId = 2
, codeReview = C
, cultureFit = A
, education = PhD }
candidate3 :: Candidate
candidate3 = Candidate { candidateId = 3
, codeReview = A
, cultureFit = B
, education = MS }
candidateDB :: Map.Map Int Candidate
candidateDB = Map.fromList ( zip [1,2,3] [candidate1,candidate2,candidate3] )
assessCandidateMaybe :: Int -> Maybe String
assessCandidateMaybe id = do
candidate <- Map.lookup id candidateDB -- <- is used because we wanna use it "normally"
let assessment = viable candidate
let statement = if (assessment) then "passed"
else "failed"
return statement
assessCandidateMaybe3 :: Int -> Maybe String
assessCandidateMaybe3 id =
(Map.lookup id candidateDB) >>=
( return . viable ) >>=
( \ x -> if (x) then return "passed" else return "failed")
-- todo: processing candidates in the context of a list
candidates :: [Candidate]
candidates = [candidate1
,candidate2
,candidate3]
assessCandidateList :: [Candidate] -> [String]
assessCandidateList candidates = candidates >>= (return . viable ) >>= (return . show )
-- <- is actually takes the element out of the context basically
assessCandidateList2 :: [Candidate] -> [String]
assessCandidateList2 candidates = do
candidate <- candidates
let passed = viable candidate
let statement = if passed then "passed" else "failed"
return statement
{-
In DO notaation,
<- takes an element out of the context.
after which you can do operations on the indivial element -- using let expressions,
these let expressions you can also return an element,
it will put all these elements back in the context and return the value
-}
-- some experiments to learn do notation:
assessCandidateListExperiment candidates = do
candidate <- candidates
let passed = viable candidate
return passed
randomDoExperiment = do
unwrappedInput <- getLine
let value = (read unwrappedInput) * 2
return value
randomDoExperiment2 = do
unwrappedInput <- getLine
let value = (read unwrappedInput) * 2
let moreValue = value * 2
return moreValue
randomDoListExperiment myList = do
element <- myList
let myFiler = if (mod element 2 == 0 ) then element
else (element + 1)
let multiplier = (myFiler * 200) + 1
return multiplier
--------------------
-- write a truly generic function to access candidate
-- one which can take any monad
assessCandidateX :: (Monad m) => m Candidate -> m String
assessCandidateX candidateInContext = do
candidate <- candidateInContext
let isPassed = if (viable candidate) then "passed"
else "fail"
return isPassed
-- the above function will work on all sorts of types in a context
test1 = assessCandidateX readCandidate
test2 = assessCandidateX (Just candidate1)
test3 = assessCandidateX [candidate1,candidate2]