Skip to content

Commit

Permalink
102 slides: Section 5 - "do" notation
Browse files Browse the repository at this point in the history
  • Loading branch information
ilya-bobyr committed Apr 19, 2019
1 parent ba2cc97 commit 9e5493a
Show file tree
Hide file tree
Showing 4 changed files with 211 additions and 4 deletions.
91 changes: 91 additions & 0 deletions haskell_102/codelab/Codelab.hs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ may the odds always be in your favor!
{-# OPTIONS_GHC -fno-warn-unused-imports #-}
{-# OPTIONS_GHC -fno-warn-unused-matches #-}
{-# OPTIONS_GHC -fno-warn-unused-binds #-}
{-# OPTIONS_GHC -fno-warn-type-defaults #-}

module Codelab where

Expand Down Expand Up @@ -341,6 +342,96 @@ countScore c1 c2 = codelab



{- #####################################################################
SECTION 5: "do" notation
While "do" notation is commonly used for "imperative" code, it can
be used with anything that is a monad (or even an applicative). As
we have seen in the slides, lists are monads, meaning we can use
"do" notation to write our list generators in a very flexible way!
For example, a generator like this:
[x + 1 | x <- [1..10]]
can be written using "do" notation like this:
do x <- [1..10]
return $ x + 1
Code inside the "do" block must evaluate to a "wrapped" value,
which would be a list in our case. If you want to return just one
value, you can use the "return" function to wrap a single value to
produce a value in the context.
-}

-- [5.1]
-- Can you rewrite allColors to use the "do" notation?
--
-- Note that inside "do" blocks Haskell switches to indentation
-- sensitive mode! It is like Python but better, as the rules are
-- more nuanced. Make sure you follow the indendation proposed in the
-- examples.

allCodesDo :: Int -> [Code]
allCodesDo s
| s < 0 = error "allCodes: size was lower than 0"
| s == 0 = codelab
| otherwise = do color <- codelab
code <- codelab
return codelab

-- [5.2]
-- Unlike generators, a "do" block can return any wrapped value. For
-- lists, it means it can return any list, not necessarily a list of
-- length 1. Let's build a "generator" using the "do" notation that
-- would produce a list of duplicates of the specified length. For
-- example, for length 5 it should produce
--
-- [1, 1, 2, 2, 3, 3, 4, 4, 5, 5]

duplicatesList :: Int -> [Int]
duplicatesList len =
do i <- [1..codelab]
codelab

-- [5.3]
-- How about the case when the length of different "blocks" would be
-- different? Let's build a "generator" similar to the previous one,
-- but that would duplicate only odd values. For example, for length
-- 5 it should produce
--
-- [1, 1, 2, 3, 3, 4, 5, 5]
--
-- I this, this is not something that can be easily expressed with
-- ordinary generator, is it?
--
-- Do not forget about Hoogle, should you need a new function.
--
-- Hint: Should you use a conditional expression, make sure to put it
-- in one line, as we did not discuss correct indentation rules for
-- that case.

oddlyDuplicateList :: Int -> [Int]
oddlyDuplicateList len =
do codelab

-- When you solve [5.3], think about the fact that when coding in "do"
-- notation you have the full power of the language, but you are
-- building something like a generator. For imperative code that uses
-- IO, your generator is producing sequences of actions that your
-- application needs to execute to actually acheive the desired
-- result.
--
-- This approach provides an amazing level of flexibility in terms of
-- abstraction. You can apply all the abstraction, code sharing and
-- other techniques to the code that produces imperatives actions, as
-- you normally apply to the code that is building lists.





{- #####################################################################
THE END
Expand Down
87 changes: 87 additions & 0 deletions haskell_102/codelab/Solution.hs
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,93 @@ countScore c1 c2 = Score black white



{- #####################################################################
SECTION 5: "do" notation
While "do" notation is commonly used for "imperative" code, it can
be used with anything that is a monad (or even applicative). As we
have seen in the lecture lists are monads, meaning we can use "do"
notation to write our list generators in a very flexible way!
For example, a generator like this:
[x + 1 | x <- [1..10]]
can be written using "do" notation like this:
do x <- [1..10]
x + 1
-}

-- [5.1]
-- Can you rewrite allColors to use the "do" notation?
--
-- Note that inside "do" blocks Haskell switches to indentation
-- sensitive mode! It is like Python but better, as the rules are
-- more nuanced. Make sure you follow the indendation proposed in the
-- examples.

allCodesDo :: Int -> [Code]
allCodesDo s
| s < 0 = error "allCodes: size was lower than 0"
| s == 0 = [[]]
| otherwise = do color <- allColors
code <- allCodesDo (s - 1)
return $ color:code

-- [5.2]
-- Unlike generators, a "do" block can return any wrapped value. For
-- lists, it means it can return any list, not necessarily a list of
-- length 1. Let's build a "generator" using the "do" notation that
-- would produce a list of duplicates of the specified length. For
-- example, for length 5 it should produce
--
-- [1, 1, 2, 2, 3, 3, 4, 4, 5, 5]

duplicatesList :: Int -> [Int]
duplicatesList len =
do i <- [1..len]
[i, i]


-- [5.3]
-- How about the case when the length of different "blocks" would be
-- different? Let's build a "generator" similar to the previous one,
-- but that would duplicate only odd values. For example, for length
-- 5 it should produce
--
-- [1, 2, 2, 3, 4, 4, 5]
--
-- I this, this is not something that can be easily expressed with
-- ordinary generator, is it?
--
-- Hint: Should you use a conditional expression, make sure to put it
-- in one line, as we did not discuss correct indentation rules for
-- that case.

oddlyDuplicateList :: Int -> [Int]
oddlyDuplicateList len =
do i <- [1..len]
if odd i
then [i, i]
else [i]

-- When you solve [5.3], think about the fact that when coding in "do"
-- notation you have the full power of the language, but you are
-- building something like a generator. For imperative code that uses
-- IO, your generator is producing sequences of actions that your
-- application needs to execute to actually acheive the desired
-- result.
--
-- This approach provides an amazing level of flexibility in terms of
-- abstraction. You can apply all the abstraction, code sharing and
-- other techniques to the code that produces imperatives actions, as
-- you normally apply to the code that is building lists.





{- #####################################################################
THE END
Expand Down
31 changes: 30 additions & 1 deletion haskell_102/codelab/Tests.hs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ newtype Section = Section Int

instance Bounded Section where
minBound = Section 1
maxBound = Section 4
maxBound = Section 5

instance Ord Section where
compare (Section s1) (Section s2) = compare s1 s2
Expand Down Expand Up @@ -241,6 +241,29 @@ t45_3 = test "[4.5] countScore [B,B,C,G] [B,B,C,G]" (Score 4 0) $ countScore [B



{- #####################################################################
SECTION 5: "do" notation
-}

t51_1 = test "[5.1] # codes of size 0: 1" 1 $ length $ allCodesDo 0
t51_2 = test "[5.1] # codes of size 1: 6" 6 $ length $ allCodesDo 1
t51_3 = test "[5.1] # codes of size 4: 1296" 1296 $ length $ allCodesDo 4
t51_4 = test "[5.1] all codes 0 have size 0" [0] $ nub $ length <$> allCodesDo 0
t51_5 = test "[5.1] all codes 1 have size 1" [1] $ nub $ length <$> allCodesDo 1
t51_6 = test "[5.1] all codes 4 have size 4" [4] $ nub $ length <$> allCodesDo 4
t51_7 = test "[5.1] no duplicated codes" True $ on (==) length (allCodesDo 4) (nub $ allCodesDo 4)

t52_1 = test "[5.2] len: 0: []" [] $ duplicatesList 0
t52_2 = test "[5.2] len: 3: [1, 1, 2, 2, 3, 3]" [1, 1, 2, 2, 3, 3] $ duplicatesList 3

t53_1 = test "[5.3] len: 0: []" [] $ oddlyDuplicateList 0
t53_2 = test "[5.3] len: 3: [1, 1, 2, 3, 3]" [1, 1, 2, 3, 3] $ oddlyDuplicateList 3
t53_3 = test "[5.3] len: 5: [1, 1, 2, 3, 3, 4, 5, 5]" [1, 1, 2, 3, 3, 4, 5, 5] $ oddlyDuplicateList 5





{- #####################################################################
Main
-}
Expand Down Expand Up @@ -275,6 +298,12 @@ tests sections =
, t44_1, t44_2, t44_3, t44_4, t44_5
, t45_1, t45_2, t45_3
]
Section 5 ->
[ display "#### Section 5"
, t51_1, t51_2, t51_3, t51_4, t51_5, t51_6, t51_7
, t52_1, t52_2
, t53_1, t53_2, t53_3
]
Section unexpected ->
[ display $ "Unexpected section requested: " ++ show unexpected
]
Expand Down
6 changes: 3 additions & 3 deletions haskell_102/slides/haskell_102.tex
Original file line number Diff line number Diff line change
Expand Up @@ -1297,12 +1297,12 @@ \section{Monadic syntax}
%% Codelab
\section{Codelab - Sections 3 and 4}
\section{Codelab - Sections 3, 4, and 5}
\begin{frame}
\frametitle{Codelab - Sections 3 and 4}
\frametitle{Codelab - Sections 3, 4, and 5}
\begin{itemize}
\item Codelab - Sections 3 and 4
\item Codelab - Sections 3, 4, and 5
\end{itemize}
\end{frame}
Expand Down

0 comments on commit 9e5493a

Please sign in to comment.