├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── docs ├── images │ ├── arrow-down.png │ └── octocat-small.png ├── index.html ├── javascripts │ └── scale.fix.js ├── pages │ ├── ex1-1.html │ ├── ex1-2.html │ ├── ex1-3.html │ ├── ex1-3hint.html │ ├── ex1-4.html │ ├── ex1-5.html │ ├── ex1-6.html │ ├── ex2-1.html │ ├── ex2-2.html │ ├── ex2-3.html │ ├── ex2-4.html │ ├── ex2-5.html │ ├── ex2-6.html │ ├── ex3-1.html │ ├── ex3-2.html │ ├── ex3-3.html │ ├── ex3-4.html │ ├── ex3-5.html │ ├── ex4-1.html │ ├── ex4-2.html │ ├── ex4-3.html │ ├── ex4-4.html │ ├── ex4-5.html │ ├── ex4-6.html │ ├── ex5-1.html │ ├── ex5-2.html │ ├── ex5-3.html │ ├── ex5-4.html │ ├── ex5-5.html │ ├── set1.html │ ├── set2.html │ ├── set3.html │ ├── set4.html │ └── set5.html └── stylesheets │ ├── github-light.css │ └── styles.css └── monad-challenges-code ├── LICENSE ├── default.nix ├── hakyll ├── index.md ├── pages │ ├── ex1-1.md │ ├── ex1-2.md │ ├── ex1-3.md │ ├── ex1-3hint.md │ ├── ex1-4.md │ ├── ex1-5.md │ ├── ex1-6.md │ ├── ex2-1.md │ ├── ex2-2.md │ ├── ex2-3.md │ ├── ex2-4.md │ ├── ex2-5.md │ ├── ex2-6.md │ ├── ex3-1.md │ ├── ex3-2.md │ ├── ex3-3.md │ ├── ex3-4.md │ ├── ex3-5.md │ ├── ex4-1.md │ ├── ex4-2.md │ ├── ex4-3.md │ ├── ex4-4.md │ ├── ex4-5.md │ ├── ex4-6.md │ ├── ex5-1.md │ ├── ex5-2.md │ ├── ex5-3.md │ ├── ex5-4.md │ ├── ex5-5.md │ ├── set1.md │ ├── set2.md │ ├── set3.md │ ├── set4.md │ └── set5.md ├── regen.sh ├── site.hs └── templates │ └── default.html ├── monad-challenges-code.cabal └── src └── MCPrelude.hs /.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | dist-newstyle 3 | *.hi 4 | *.o 5 | solutions 6 | *.swp 7 | _site 8 | _cache 9 | hakyll/site 10 | src/Set[1-5].hs 11 | .ghc.environment.* 12 | result 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016, Doug Beardsley 2 | 3 | All rights reserved. 4 | 5 | Haskell code in this project uses the following license: 6 | 7 | Redistribution and use in source and binary forms, with or without 8 | modification, are permitted provided that the following conditions are met: 9 | 10 | * Redistributions of source code must retain the above copyright 11 | notice, this list of conditions and the following disclaimer. 12 | 13 | * Redistributions in binary form must reproduce the above 14 | copyright notice, this list of conditions and the following 15 | disclaimer in the documentation and/or other materials provided 16 | with the distribution. 17 | 18 | * Neither the name of Doug Beardsley nor the names of other 19 | contributors may be used to endorse or promote products derived 20 | from this software without specific prior written permission. 21 | 22 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 23 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 24 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 25 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 26 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 27 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 28 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 29 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 30 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 31 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 32 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # The Monad Challenges 2 | 3 | The goal of this project is to provide a roadmap for hands-on exploration and 4 | experimentation with monads. It is still rough and there is a fair amount 5 | content that could be added. 6 | 7 | ## Contribution Guide 8 | 9 | The site is a static site generated with 10 | [Hakyll](http://hackage.haskell.org/package/hakyll). To make edits to the text 11 | or to add new sets/exercises, you should edit the markdown files in 12 | [monad-challenges-code/hakyll/pages](https://github.com/mightybyte/monad-challenges/tree/master/hakyll/pages). 13 | 14 | To build, run `nix-build` from the `monad-challenges-code` directory. 15 | 16 | To regenerate the HTML, go to the `monad-challenges-code/hakyll` directory and 17 | run `./regen.sh`. 18 | 19 | If you want to contribute, but don't know what to do, check out the [open 20 | issues](https://github.com/mightybyte/monad-challenges/issues) and see if you 21 | can help with anything there. 22 | -------------------------------------------------------------------------------- /docs/images/arrow-down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mightybyte/monad-challenges/ca9f7f680ad3e11ee2d006be8e055ed2139e0364/docs/images/arrow-down.png -------------------------------------------------------------------------------- /docs/images/octocat-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mightybyte/monad-challenges/ca9f7f680ad3e11ee2d006be8e055ed2139e0364/docs/images/octocat-small.png -------------------------------------------------------------------------------- /docs/javascripts/scale.fix.js: -------------------------------------------------------------------------------- 1 | fixScale = function(doc) { 2 | 3 | var addEvent = 'addEventListener', 4 | type = 'gesturestart', 5 | qsa = 'querySelectorAll', 6 | scales = [1, 1], 7 | meta = qsa in doc ? doc[qsa]('meta[name=viewport]') : []; 8 | 9 | function fix() { 10 | meta.content = 'width=device-width,minimum-scale=' + scales[0] + ',maximum-scale=' + scales[1]; 11 | doc.removeEventListener(type, fix, true); 12 | } 13 | 14 | if ((meta = meta[meta.length - 1]) && addEvent in doc) { 15 | fix(); 16 | scales = [.25, 1.6]; 17 | doc[addEvent](type, fix, true); 18 | } 19 | 20 | }; -------------------------------------------------------------------------------- /docs/pages/ex1-1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | The Monad Challenges 6 | 7 | 8 | 9 | 10 | 11 | 14 | 15 | 16 |
17 |
18 |

The Monad Challenges

19 |

A set of challenges for jump starting your understanding of monads.

20 | 21 |

Outline

22 | 23 | 24 |

Set 1: Random Numbers

25 | 26 |

Set 2: Failing Computations

27 | 28 |

Set 3: Combinations

29 | 30 |

Set 4: Common Abstraction

31 | 32 |

Set 5: Do Notation

33 | 34 | 35 | 40 | 41 |

This project is maintained by mightybyte

42 | 43 |

44 | Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. 45 |

46 | 47 |
48 |
49 |

Random Number Generation

50 | 51 |

In MCPrelude we provide a simple random number generation function rand. Random number generators usually rely on mutable state side effects. They maintain some state somewhere in memory and use that to figure out what “random” number to give you. Before the function returns, it modifies the mutable state so that the next time you call the function you’ll get a different number. Haskell is a pure functional programming language and because our custom Prelude hides Haskell’s mechanisms for dealing with side effects, we can’t build a random number generator that way. Our random number generator has to have everything it needs passed in and it has to return everything it modifies. Therefore, it has this type signature:

52 |
rand :: Seed -> (Integer, Seed)
53 |

You can construct seeds with the mkSeed function.

54 |
mkSeed :: Integer -> Seed
55 |

Make a function that gives you the first five random numbers starting with a seed of (mkSeed 1). Call it:

56 |
fiveRands :: [Integer]
57 |

For now don’t try to do anything fancy. Just implement it in the most straightforward way that comes to mind. To check your answers, the product of these numbers is 8681089573064486461641871805074254223660.

58 |

Previous Page - Next Page

59 | 60 |
61 |
62 | 63 | 67 | 73 | 74 | -------------------------------------------------------------------------------- /docs/pages/ex1-2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | The Monad Challenges 6 | 7 | 8 | 9 | 10 | 11 | 14 | 15 | 16 |
17 |
18 |

The Monad Challenges

19 |

A set of challenges for jump starting your understanding of monads.

20 | 21 |

Outline

22 | 23 | 24 |

Set 1: Random Numbers

25 | 26 |

Set 2: Failing Computations

27 | 28 |

Set 3: Combinations

29 | 30 |

Set 4: Common Abstraction

31 | 32 |

Set 5: Do Notation

33 | 34 | 35 | 40 | 41 |

This project is maintained by mightybyte

42 | 43 |

44 | Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. 45 |

46 | 47 |
48 |
49 |

Random Character Generation

50 | 51 |

Now use the rand function to make your own function for generating random letters of the alphabet. It should only generate lower case letters a-z.

52 |

We supply a function in MCPrelude called toLetter. Use the toLetter function to write this random letter function:

53 |
randLetter :: Seed -> (Char, Seed)
54 |

Now use this function to write another function randString3 that generates a random string of three letters using an initial seed of 1.

55 |
randString3 :: String
56 |

When you use this site to calculate the SHA-256 hash of the output of randString3, you get 9d475eb78d3e38085220ed6ebde9d8f7d26540bb1c8f9382479c3acd4c8c94a3.

57 |

Previous Page - Next Page

58 | 59 |
60 |
61 | 62 | 66 | 72 | 73 | -------------------------------------------------------------------------------- /docs/pages/ex1-3.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | The Monad Challenges 6 | 7 | 8 | 9 | 10 | 11 | 14 | 15 | 16 |
17 |
18 |

The Monad Challenges

19 |

A set of challenges for jump starting your understanding of monads.

20 | 21 |

Outline

22 | 23 | 24 |

Set 1: Random Numbers

25 | 26 |

Set 2: Failing Computations

27 | 28 |

Set 3: Combinations

29 | 30 |

Set 4: Common Abstraction

31 | 32 |

Set 5: Do Notation

33 | 34 | 35 | 40 | 41 |

This project is maintained by mightybyte

42 | 43 |

44 | Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. 45 |

46 | 47 |
48 |
49 |

More Generators

50 | 51 |

Now write a few more functions to generate different subsets of random numbers. But first, we need to generalize a bit. If we look at the type signatures for rand and randLetter, what are the commonalities?

52 |
rand :: Seed -> (Integer, Seed)
53 | randLetter :: Seed -> (Char, Seed)
54 |

This common pattern can be captured in a type synonym. The key point here is that they use two different types, so you will have to introduce a type parameter. Create a type synonym that would allow us to change the type signatures of these two functions to the following:

55 |
rand :: Gen Integer
56 | randLetter :: Gen Char
57 |

This is a relatively obvious type synonym and it is pretty easy to write, but it enables a big mental leap. Now that we have this type synonym we’ve jumped up a level of abstraction and higher level patterns will become more apparent. First go back and rewrite all your existing type signatures using this Gen type synonym. Now write three new functions:

58 |
randEven :: Gen Integer -- the output of rand * 2
59 | randOdd :: Gen Integer -- the output of rand * 2 + 1
60 | randTen :: Gen Integer -- the output of rand * 10
61 |

Where randEven and randOdd only generate even and odd numbers respectively, randTen only generates multiples of 10. Write randEven in terms of rand, write randOdd in terms of randEven, and write randTen however you want. There’s a general pattern lurking here. If you implement all these functions the naive way like you did fiveRands, you’ll be repeating the same pattern over and over. Figure out how to exploit the common pattern and write a function called generalA that implements this pattern. All three of the above functions will also use generalA. There are a number of different ways you could abstract this. We realize that you may not pick the right abstraction. Play around with as many different possibilities as you can think of.

62 |

SPEND SOME TIME WITH THIS BEFORE READING THE HINT!

63 |

Play with a number of abstractions and try to find the most flexible one. After you’ve spent some time with it, look at the hint here.

64 |

Pass all three of these functions a seed of 1. They’ll give you three numbers back, the product of which is 189908109902700.

65 |

Previous Page - Next Page

66 | 67 |
68 |
69 | 70 | 74 | 80 | 81 | -------------------------------------------------------------------------------- /docs/pages/ex1-3hint.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | The Monad Challenges 6 | 7 | 8 | 9 | 10 | 11 | 14 | 15 | 16 |
17 |
18 |

The Monad Challenges

19 |

A set of challenges for jump starting your understanding of monads.

20 | 21 |

Outline

22 | 23 | 24 |

Set 1: Random Numbers

25 | 26 |

Set 2: Failing Computations

27 | 28 |

Set 3: Combinations

29 | 30 |

Set 4: Common Abstraction

31 | 32 |

Set 5: Do Notation

33 | 34 | 35 | 40 | 41 |

This project is maintained by mightybyte

42 | 43 |

44 | Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. 45 |

46 | 47 |
48 |
49 |

First Generalization Hint

50 | 51 |

All three of these functions are conceptually a call to rand followed by applying a function to the Integer it returns. What you ultimately need is a function that you can pass a Gen a to and will transform the result.

52 |

Be careful not to make your function too specific to rand. rand is a specific example of a Gen Integer but you want your function to work for other kinds of Gen a as well.

53 |

If you are still stuck, you can view the expected type signature by hex decoding the following string: 67656E6572616C41203A3A202861202D3E206229202D3E2047656E2061202D3E2047656E2062

54 |

You can decode it with this online hex decoder.

55 |

Back

56 | 57 |
58 |
59 | 60 | 64 | 70 | 71 | -------------------------------------------------------------------------------- /docs/pages/ex1-4.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | The Monad Challenges 6 | 7 | 8 | 9 | 10 | 11 | 14 | 15 | 16 |
17 |
18 |

The Monad Challenges

19 |

A set of challenges for jump starting your understanding of monads.

20 | 21 |

Outline

22 | 23 | 24 |

Set 1: Random Numbers

25 | 26 |

Set 2: Failing Computations

27 | 28 |

Set 3: Combinations

29 | 30 |

Set 4: Common Abstraction

31 | 32 |

Set 5: Do Notation

33 | 34 | 35 | 40 | 41 |

This project is maintained by mightybyte

42 | 43 |

44 | Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. 45 |

46 | 47 |
48 |
49 |

Generalizing Random Pairs

50 | 51 |

Use the provided rand function and your randLetter function to make a function that generates a random pair of a Char and an Integer.

52 |
randPair :: Gen (Char, Integer)
53 |

Generate the letter first, then generate the number using the seed acquired from generating the letter. When you give this function (mkSeed 1) it gives you the random pair ('l',282475249).

54 |

Here we’re starting to see the Gen type synonym pay off. Without Gen, the type would have been:

55 |
randPair :: Seed -> ((Char, Integer), Seed)
56 |

Still understandable, but the nested tuples are starting to obscure things slightly.

57 |

You have just composed two generators. Now generalize the composition to generalPair, and make sure that the second argument receive the seed generated by the first argument.

58 |
generalPair :: Gen a -> Gen b -> Gen (a,b)
59 |

This function makes the Gen type synonym almost essential. Here’s what we would have had to write if we didn’t have Gen:

60 |
generalPair :: (Seed -> (a, Seed)) -> (Seed -> (b, Seed)) -> (Seed -> ((a,b), Seed))
61 |

Removing the unnecessary parentheses gets us this type signature, which might help you a little when implementing the function.

62 |
generalPair :: (Seed -> (a, Seed)) -> (Seed -> (b, Seed)) -> Seed -> ((a,b), Seed)
63 |

Write another version randPair_ using generalPair, and test it by comparing its output to what you got from randPair.

64 |

Generalizing Pairs Even More

65 |

This generalPair function can be generalized even more. Instead of always constructing pairs, you should be able to have a generalization that can construct anything. Your result shouldn’t be fixed to Gen (a,b). It should also be able to be Gen String, Gen Polynomial, or Gen BlogPost. All you need to do is pass in a function that does the constructing with two inputs. Call this even more generalized function generalB. Once you have it implemented, write a new generalPair2 function in terms of generalB.

66 |

Previous Page - Next Page

67 | 68 |
69 |
70 | 71 | 75 | 81 | 82 | -------------------------------------------------------------------------------- /docs/pages/ex1-5.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | The Monad Challenges 6 | 7 | 8 | 9 | 10 | 11 | 14 | 15 | 16 |
17 |
18 |

The Monad Challenges

19 |

A set of challenges for jump starting your understanding of monads.

20 | 21 |

Outline

22 | 23 | 24 |

Set 1: Random Numbers

25 | 26 |

Set 2: Failing Computations

27 | 28 |

Set 3: Combinations

29 | 30 |

Set 4: Common Abstraction

31 | 32 |

Set 5: Do Notation

33 | 34 | 35 | 40 | 41 |

This project is maintained by mightybyte

42 | 43 |

44 | Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. 45 |

46 | 47 |
48 |
49 |

Generalizing Lists of Generators

50 | 51 |

By now you have probably realized that generating multiple random numbers this way is rather painful. You have to thread the output seed from one rand call to the input of the next call. This is tedious and error prone, so now you will create a function to make this a little easier.

52 |
repRandom :: [Gen a] -> Gen [a]
53 |

This function lets you give it a list of generators and it automatically handles the state threading for you.

54 |

The nice thing about this function is that [Gen a] is really general, so it composes well with other built-in list functions. For example:

55 |
repRandom (replicate 3 randLetter) (mkSeed 1)
56 |

This function should generate the same three letters that you got from randString3 in challenge #2.

57 |

Previous Page - Next Page

58 | 59 |
60 |
61 | 62 | 66 | 72 | 73 | -------------------------------------------------------------------------------- /docs/pages/ex1-6.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | The Monad Challenges 6 | 7 | 8 | 9 | 10 | 11 | 14 | 15 | 16 |
17 |
18 |

The Monad Challenges

19 |

A set of challenges for jump starting your understanding of monads.

20 | 21 |

Outline

22 | 23 | 24 |

Set 1: Random Numbers

25 | 26 |

Set 2: Failing Computations

27 | 28 |

Set 3: Combinations

29 | 30 |

Set 4: Common Abstraction

31 | 32 |

Set 5: Do Notation

33 | 34 | 35 | 40 | 41 |

This project is maintained by mightybyte

42 | 43 |

44 | Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. 45 |

46 | 47 |
48 |
49 |

Threading the random number state

50 | 51 |

In the previous exercise we wrote something that handled the threading of state through a list of generators. A simpler idea is to have a function that does one step of two generators and the necessary state threading. Now write a function called genTwo that does this. Its first argument will be a generator. Its second argument will be a function that takes the result of the first generator and returns a second generator. The type signature looks like this:

52 |
genTwo :: Gen a -> (a -> Gen b) -> Gen b
53 |

Implement this function.

54 |

Now look at the implementation of your repRandom function. It probably has one clause handling the empty list case. That case probably looks something like this:

55 |
repRandom [] s = ([], s)
56 |

repRandom was expecting a list of generators and it’s supposed to return a generator. In the empty list case it has no incoming generators to work with but it still has to return one. Essentially what’s happening here is that it has to construct a Gen out of thin air. It turns out that this is a really common pattern. So let’s make a function for it. We’ll call this function mkGen. It has to return a Gen a. But it has to get the a from somewhere, so that will have to be the argument.

57 |

Implement mkGen. Try to figure out the type signature yourself, but if you need help here it is hex-encoded: 6D6B47656E203A3A2061202D3E2047656E2061. You can decode it with this online hex decoder.

58 |

Congrats! You have finished the first set. There are a few repeating patterns lurking around. Lets see more examples in the following two sets, and we will provide a unified approach in Set 4. Don’t jump ahead! We have not seen all of them!

59 |

Previous Page - Next Page

60 | 61 |
62 |
63 | 64 | 68 | 74 | 75 | -------------------------------------------------------------------------------- /docs/pages/ex2-1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | The Monad Challenges 6 | 7 | 8 | 9 | 10 | 11 | 14 | 15 | 16 |
17 |
18 |

The Monad Challenges

19 |

A set of challenges for jump starting your understanding of monads.

20 | 21 |

Outline

22 | 23 | 24 |

Set 1: Random Numbers

25 | 26 |

Set 2: Failing Computations

27 | 28 |

Set 3: Combinations

29 | 30 |

Set 4: Common Abstraction

31 | 32 |

Set 5: Do Notation

33 | 34 | 35 | 40 | 41 |

This project is maintained by mightybyte

42 | 43 |

44 | Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. 45 |

46 | 47 |
48 |
49 |

The Maybe Type

50 | 51 |

You may have noticed that MCPrelude doesn’t have the Maybe type or anything from Data.Maybe. That’s because you’re going to build it all yourself.

52 |

IMPORTANT Again, it is imperative that you DO NOT CHEAT. Don’t look at any of the Maybe stuff from Prelude or Data.Maybe. Don’t do it. Nobody is forcing you to do these exercises, so you should try to get the maximum possible benefit. IMPORTANT

53 |

First of all, you need to define the Maybe type. It should be able to represent any value a, as well as the case where no value a exists. This type needs to represent failing values of any type, so it needs a type variable similar to what we saw in the Gen type synonym. But this can’t be a type synonym because it has two constructors. Write this type yourself and get it to compile. Once you’ve gotten it compiling, check your answer by hex decoding the following:

54 |
64617461204D617962652061203D204E6F7468696E67207C204A7573742061
55 |

You should use this definition and names going forward. We just wanted you to work on it yourself first.

56 |

Then for convenience write a Show instance for this new type.

57 |
instance Show a => Show (Maybe a) where
58 |   -- show :: Maybe a -> String
59 |   show = undefined
60 |

If you’re having trouble with this, here is the hex encoded instance.

61 |
202073686F77204E6F7468696E67203D20224E6F7468696E6722DA202073686F7720284A757374206129203D20224A7573742022202B2B2073686F772061
62 |

You are also going to need an Eq instance as well.

63 |
instance Eq a => Eq (Maybe a) where
64 |   -- (==) :: Maybe a -> Maybe a -> Bool
65 |   (==) = undefined
66 |

We’re not going to give you the answer to this one. In the worst case scenario you should be able to figure it out from the way the Show instance was done.

67 |

On the other hand, if writing these instances was too difficult, it might be good to go study some more introductory Haskell materials before continuing here.

68 |

Previous Page - Next Page

69 | 70 |
71 |
72 | 73 | 77 | 83 | 84 | -------------------------------------------------------------------------------- /docs/pages/ex2-2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | The Monad Challenges 6 | 7 | 8 | 9 | 10 | 11 | 14 | 15 | 16 |
17 |
18 |

The Monad Challenges

19 |

A set of challenges for jump starting your understanding of monads.

20 | 21 |

Outline

22 | 23 | 24 |

Set 1: Random Numbers

25 | 26 |

Set 2: Failing Computations

27 | 28 |

Set 3: Combinations

29 | 30 |

Set 4: Common Abstraction

31 | 32 |

Set 5: Do Notation

33 | 34 | 35 | 40 | 41 |

This project is maintained by mightybyte

42 | 43 |

44 | Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. 45 |

46 | 47 |
48 |
49 |

Build a library of things that can fail

50 | 51 |

Now that you have a Maybe type, you need to use it. So now use it to build safe versions of several common functions specified by the Prelude.

52 |
headMay :: [a] -> Maybe a
53 | tailMay :: [a] -> Maybe [a]
54 | lookupMay :: Eq a => a -> [(a, b)] -> Maybe b
55 | divMay :: (Eq a, Fractional a) => a -> a -> Maybe a
56 | maximumMay :: Ord a => [a] -> Maybe a
57 | minimumMay :: Ord a => [a] -> Maybe a
58 |

The functions headMay and tailMay are “safe” versions of the well known head and tail functions. The former returns the first element of a list or Nothing if the list is empty. The latter returns a list containing all but the first element of a list, or Nothing if the list is empty. The lookupMay function is kind of like the lookup for a map. Find the first tuple in the list where the first element is the equal to the passed in value and return the second element. If there is no matching a, then return Nothing. The divMay function should return Nothing if you’re dividing by 0 and the result of the division otherwise. maximumMay and minimumMay calculate the maximum and minimum respectively of all the numbers in the list, but if the list is empty they return Nothing.

59 |

Previous Page - Next Page

60 | 61 |
62 |
63 | 64 | 68 | 74 | 75 | -------------------------------------------------------------------------------- /docs/pages/ex2-3.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | The Monad Challenges 6 | 7 | 8 | 9 | 10 | 11 | 14 | 15 | 16 |
17 |
18 |

The Monad Challenges

19 |

A set of challenges for jump starting your understanding of monads.

20 | 21 |

Outline

22 | 23 | 24 |

Set 1: Random Numbers

25 | 26 |

Set 2: Failing Computations

27 | 28 |

Set 3: Combinations

29 | 30 |

Set 4: Common Abstraction

31 | 32 |

Set 5: Do Notation

33 | 34 | 35 | 40 | 41 |

This project is maintained by mightybyte

42 | 43 |

44 | Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. 45 |

46 | 47 |
48 |
49 |

Chains of Failing Computations

50 | 51 |

In MCPrelude we have defined the following type synonym for you.

52 |
type GreekData = [(String, [Integer])]
53 |

We also provide two data structures greekDataA, greekDataB :: GreekData that have some sample data.

54 |

Write a function to query the above data structures. Your function should have the following type signature:

55 |
queryGreek :: GreekData -> String -> Maybe Double
56 |

Your implementation of this function should use the functions you wrote in the previous exercise to do the following: first query the GreekData that is passed in, look up the string passed in the second argument, and retrieve the corresponding list of Integers. Call this list xs. Next calculate the maximum of the tail of xs. (Don’t use any pattern matching here. Use case expressions and the maximumMay and tailMay functions you wrote in the last exercise.) Take the maximum and divide it by the head of the list (using your headMay and divMay functions). If any of these operations along the way return Nothing, then your function should return Nothing. But if everything succeeds, then return the final quotient. One hint… you’ll need to use the fromIntegral function to convert your two Integers to Doubles for the final call to divMay.

57 |

You will probably find this function pretty annoying to implement. Stick with us though… there is a point. The more you feel the pain now, the more the solutions will stick in your head later. Ultimately, we will get rid of the repeating part. But don’t jump ahead! We haven’t seen all patterns.

58 |

Your function should generate the following results:

59 |
queryGreek greekDataA "alpha" == Just 2.0
60 | queryGreek greekDataA "beta" == Nothing
61 | queryGreek greekDataA "gamma" == Just 3.3333333333333335
62 | queryGreek greekDataA "delta" == Nothing
63 | queryGreek greekDataA "zeta" == Nothing
64 | 
65 | queryGreek greekDataB "rho" == Nothing
66 | queryGreek greekDataB "phi" == Just 0.24528301886792453
67 | queryGreek greekDataB "chi" == Just 9.095238095238095
68 | queryGreek greekDataB "psi" == Nothing
69 | queryGreek greekDataB "omega" == Just 24.0
70 |

If your function threw any kind of exception on any of those inputs, then your implementation is wrong. Make sure your function always returns a Nothing or a Just in every case.

71 |

Previous Page - Next Page

72 | 73 |
74 |
75 | 76 | 80 | 86 | 87 | -------------------------------------------------------------------------------- /docs/pages/ex2-6.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | The Monad Challenges 6 | 7 | 8 | 9 | 10 | 11 | 14 | 15 | 16 |
17 |
18 |

The Monad Challenges

19 |

A set of challenges for jump starting your understanding of monads.

20 | 21 |

Outline

22 | 23 | 24 |

Set 1: Random Numbers

25 | 26 |

Set 2: Failing Computations

27 | 28 |

Set 3: Combinations

29 | 30 |

Set 4: Common Abstraction

31 | 32 |

Set 5: Do Notation

33 | 34 | 35 | 40 | 41 |

This project is maintained by mightybyte

42 | 43 |

44 | Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. 45 |

46 | 47 |
48 |
49 |

Tailprod

50 | 51 |

Write a function that calculates the product of the tail of a list. Use your tailMay function and the product function defined in the Prelude. This function should return Nothing when passed an empty list, Just 1 when passed a list of one element (which is what the Prelude’s product function does), and return the product of the tail for larger lists.

52 |
tailProd :: Num a => [a] -> Maybe a
53 |

Now write a similar but slightly different function:

54 |
tailSum :: Num a => [a] -> Maybe a
55 |

These two functions have a lot in common. See if you can abstract out the commonality. To do this, write another function called transMaybe that is the generalized version of both of these. Spend some time working on this before you look at the next hex encoded hint:

56 |
7472616E734D61796265203A3A202861202D3E206229202D3E204D617962652061202D3E204D617962652062
57 |

Write a function with that type signature and then go back and implement tailProd and tailSum in terms of that function.

58 |

Now that you have that finished, use transMaybe again, with maximumMay and minimumMay, to write two more functions tailMax and tailMin. These functions will have different type signatures than tailProd and tailSum. See if you can figure out what they should be. If you can’t figure it out, here’s a hex encoded hint.

59 |
7461696C4D6178203A3A204F72642061203D3E205B615D202D3E204D6179626520284D61796265206129
60 |

That type signature is different from the ones above for tailProd and tailSum and less convenient to use. To collapse the two levels you’ll need another helper function. See if you can figure out what the type signature of this function should be and how to implement it. Call this function combine. Here is its hex encoded type signature if you need help.

61 |
636F6D62696E65203A3A204D6179626520284D61796265206129202D3E204D617962652061
62 |

Previous Page - Next Page

63 | 64 |
65 |
66 | 67 | 71 | 77 | 78 | -------------------------------------------------------------------------------- /docs/pages/ex3-1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | The Monad Challenges 6 | 7 | 8 | 9 | 10 | 11 | 14 | 15 | 16 |
17 |
18 |

The Monad Challenges

19 |

A set of challenges for jump starting your understanding of monads.

20 | 21 |

Outline

22 | 23 | 24 |

Set 1: Random Numbers

25 | 26 |

Set 2: Failing Computations

27 | 28 |

Set 3: Combinations

29 | 30 |

Set 4: Common Abstraction

31 | 32 |

Set 5: Do Notation

33 | 34 | 35 | 40 | 41 |

This project is maintained by mightybyte

42 | 43 |

44 | Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. 45 |

46 | 47 |
48 |
49 |

Generating combinations

50 | 51 |

Have you ever needed to generate the Cartesian product of some things? Let’s use the term combination to mean an element from the Cartesian product. In imperative languages generating combinations always seemed like a pain to me. We are going to explore how Haskell can make combination generation easier.

52 |

First, write this function:

53 |
allPairs :: [a] -> [b] -> [(a,b)]
54 |

This function should generate all possible pairs of items from each of the two input lists. This means it should NOT have this behavior:

55 |
allPairs [1,2,3] [4,5,6] == [(1,4),(2,5),(3,6)]
56 |

That’s the zip function and it’s not what we are looking for here. Instead, your function should generate this:

57 |
allPairs [1,2] [3,4] == [(1,3),(1,4),(2,3),(2,4)]
58 |

Here is another test case:

59 |
allPairs [1..3] [6..8] == [(1,6),(1,7),(1,8),(2,6),(2,7),(2,8),(3,6),(3,7),(3,8)]
60 |

Note that because of the way we set up the project template you cannot use list comprehensions to solve this. That’s intentional. You should solve it with explicit recursion.

61 |

Previous Page - Next Page

62 | 63 |
64 |
65 | 66 | 70 | 76 | 77 | -------------------------------------------------------------------------------- /docs/pages/ex3-2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | The Monad Challenges 6 | 7 | 8 | 9 | 10 | 11 | 14 | 15 | 16 |
17 |
18 |

The Monad Challenges

19 |

A set of challenges for jump starting your understanding of monads.

20 | 21 |

Outline

22 | 23 | 24 |

Set 1: Random Numbers

25 | 26 |

Set 2: Failing Computations

27 | 28 |

Set 3: Combinations

29 | 30 |

Set 4: Common Abstraction

31 | 32 |

Set 5: Do Notation

33 | 34 | 35 | 40 | 41 |

This project is maintained by mightybyte

42 | 43 |

44 | Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. 45 |

46 | 47 |
48 |
49 |

Poker hands

50 | 51 |

We can use the allPairs function to do things like generate poker hands. In MCPrelude we have defined two lists called cardRanks and cardSuits. Try calling your allPairs function on these:

52 |
allPairs cardRanks cardSuits == [(2,"H"),(2,"D"),(2,"C"),(2,"S"),(3,"H"),(3,"D"),(3,"C"),(3,"S"),(4,"H"),(4,"D"),(4,"C"),(4,"S"),(5,"H"),(5,"D"),(5,"C"),(5,"S")]
53 |

But this isn’t a very nice representation. We want a more concise representation of the card to show our user. If you were writing a real poker-related program, instead of using a tuple you would probably create a data type Card. Do that now and then write a Show instance for it that returns the more concise representation "2H", "2D", etc.

54 |
show (Card 2 "h") == "2h"
55 |

Now create a new function allCards that does the same thing as your allPairs function but uses your new Card data type instead. It should have the following type signature:

56 |
allCards :: [Int] -> [String] -> [Card]
57 |

This function should do the same thing as allPairs, but with more concise output. When you write this function, don’t implement it using your previous allPairs function. Rewrite it.

58 |
show (allCards cardRanks cardSuits) == "[2H,2D,2C,2S,3H,3D,3C,3S,4H,4D,4C,4S,5H,5D,5C,5S]"
59 |

Previous Page - Next Page

60 | 61 |
62 |
63 | 64 | 68 | 74 | 75 | -------------------------------------------------------------------------------- /docs/pages/ex3-3.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | The Monad Challenges 6 | 7 | 8 | 9 | 10 | 11 | 14 | 15 | 16 |
17 |
18 |

The Monad Challenges

19 |

A set of challenges for jump starting your understanding of monads.

20 | 21 |

Outline

22 | 23 | 24 |

Set 1: Random Numbers

25 | 26 |

Set 2: Failing Computations

27 | 28 |

Set 3: Combinations

29 | 30 |

Set 4: Common Abstraction

31 | 32 |

Set 5: Do Notation

33 | 34 | 35 | 40 | 41 |

This project is maintained by mightybyte

42 | 43 |

44 | Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. 45 |

46 | 47 |
48 |
49 |

Generalizing pairs and cards

50 | 51 |

As we have done before, look at your allPairs and allCards functions and find the differences. Then implement a more general function that can be used to implement both allPairs and allCards. Call this new function allCombs.

52 |
allCombs :: (a -> b -> c) -> [a] -> [b] -> [c]
53 |

Then go back and reimplement allPairs and allCards in terms of allCombs. Verify that they do the same thing as the original functions.

54 |

Previous Page - Next Page

55 | 56 |
57 |
58 | 59 | 63 | 69 | 70 | -------------------------------------------------------------------------------- /docs/pages/ex3-4.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | The Monad Challenges 6 | 7 | 8 | 9 | 10 | 11 | 14 | 15 | 16 |
17 |
18 |

The Monad Challenges

19 |

A set of challenges for jump starting your understanding of monads.

20 | 21 |

Outline

22 | 23 | 24 |

Set 1: Random Numbers

25 | 26 |

Set 2: Failing Computations

27 | 28 |

Set 3: Combinations

29 | 30 |

Set 4: Common Abstraction

31 | 32 |

Set 5: Do Notation

33 | 34 | 35 | 40 | 41 |

This project is maintained by mightybyte

42 | 43 |

44 | Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. 45 |

46 | 47 |
48 |
49 |

Combinations of three things

50 | 51 |

Our allCombs function is more general but it still only lets us generate combinations of two things. Now implement an allCombs3 function that generates combinations of 3 things. Don’t try to do anything fancy yet. Just use the most straightforward approach you can think of.

52 |
allCombs3 :: (a -> b -> c -> d) -> [a] -> [b] -> [c] -> [d]
53 |

Here is example output to check your function.

54 |
allCombs3 (,,) [1,2] [3,4] [5,6] == [(1,3,5),(1,3,6),(1,4,5),(1,4,6),(2,3,5),(2,3,6),(2,4,5),(2,4,6)]
55 |

Previous Page - Next Page

56 | 57 |
58 |
59 | 60 | 64 | 70 | 71 | -------------------------------------------------------------------------------- /docs/pages/ex3-5.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | The Monad Challenges 6 | 7 | 8 | 9 | 10 | 11 | 14 | 15 | 16 |
17 |
18 |

The Monad Challenges

19 |

A set of challenges for jump starting your understanding of monads.

20 | 21 |

Outline

22 | 23 | 24 |

Set 1: Random Numbers

25 | 26 |

Set 2: Failing Computations

27 | 28 |

Set 3: Combinations

29 | 30 |

Set 4: Common Abstraction

31 | 32 |

Set 5: Do Notation

33 | 34 | 35 | 40 | 41 |

This project is maintained by mightybyte

42 | 43 |

44 | Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. 45 |

46 | 47 |
48 |
49 |

Combinations of more things

50 | 51 |

You probably noticed that allCombs3 is getting painful to write. Worse still, it does not help us generate combinations of four or more things. We need to take a different approach if we want this to scale easily. This is a difficult step, so we are not going to make you figure it out yourself. All the same, spend some time thinking about how you might do it. Here is a hint though. You probably noticed that allCombs has ([a] -> [b]) in its type signature and allCombs3 has ([a] -> [b] -> [c]) in its type signature. When you are playing with this, do not try to generalize that pattern to [[a]]. That is not an adequate generalization because it only allows a and has no b or c anywhere. Spend some time thinking about this before you continue. But don’t be discouraged if you can’t figure it out. This one is hard.

52 |

Back? If you figured it out, congratulations. If not, here is the hex encoded type signature for a function called combStep that is the generalization we need.

53 |
636F6D6253746570203A3A205B61202D3E20625D202D3E205B615D202D3E205B625D
54 |

Now write that function. Then use the combStep function to implement allCombs and allCombs3 and check that the new implementations have the same behavior as before. Once you do this it should be pretty obvious how you would use combStep to implement allCombs4 and beyond. Also notice how combStep compares to allCombs.

55 | 56 |

Previous Page - Next Page

57 | 58 |
59 |
60 | 61 | 65 | 71 | 72 | -------------------------------------------------------------------------------- /docs/pages/ex4-1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | The Monad Challenges 6 | 7 | 8 | 9 | 10 | 11 | 14 | 15 | 16 |
17 |
18 |

The Monad Challenges

19 |

A set of challenges for jump starting your understanding of monads.

20 | 21 |

Outline

22 | 23 | 24 |

Set 1: Random Numbers

25 | 26 |

Set 2: Failing Computations

27 | 28 |

Set 3: Combinations

29 | 30 |

Set 4: Common Abstraction

31 | 32 |

Set 5: Do Notation

33 | 34 | 35 | 40 | 41 |

This project is maintained by mightybyte

42 | 43 |

44 | Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. 45 |

46 | 47 |
48 |
49 |

Generalizing State and Maybe

50 | 51 |

Now that you’ve spent some time building general abstractions for working with state and failing computations we want to go back through and look for commonalities.

52 |

First, go back through all the code you wrote for Set 1, Find all the generalized functions and copy their type signatures (not the code) into a new file. You can skip the functions that solved special case exercises. Then do the same thing for Set 2 and copy those type signatures into the same file.

53 |

Now look at these type signatures and look for functions from Set 1 that have a similar type signature pattern to functions from Set 2. The type signatures obviously won’t be the same, but there are similar patterns in both sets. You should come up with two pairs of function equivalences.

54 |

Here is the hex encoded answer. But again, spend some effort trying to see this correspondence for yourself.

55 |
67656E54776F207E3D206C696E6B2C2067656E6572616C42207E3D20794C696E6B
56 |

Now that you have the correspondences, for each pair write a more general type signature that works for both the Gen version and the Maybe version. Think back to things we’ve done before. If two signatures are exactly the same except for one difference, replace that difference with a type variable. It doesn’t matter what letter you use for the type variable as long as it is different from all the other type variables that occur in the signature. But just to make your life easier we’ll give you a hint: use the letter m. Here’s the hex-encoded answer:

57 |
67656E54776F2C206C696E6B0A20203A3A206D2061202D3E202861202D3E206D206229202D3E206D20620A0A67656E6572616C422C20794C696E6B0A20203A3A202861202D3E2062202D3E206329202D3E206D2061202D3E206D2062202D3E206D2063
58 |

You won’t be able to implement these functions yet. We’re just writing type signatures right now and looking at the patterns involved.

59 |

Previous Page - Next Page

60 | 61 |
62 |
63 | 64 | 68 | 74 | 75 | -------------------------------------------------------------------------------- /docs/pages/ex4-2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | The Monad Challenges 6 | 7 | 8 | 9 | 10 | 11 | 14 | 15 | 16 |
17 |
18 |

The Monad Challenges

19 |

A set of challenges for jump starting your understanding of monads.

20 | 21 |

Outline

22 | 23 | 24 |

Set 1: Random Numbers

25 | 26 |

Set 2: Failing Computations

27 | 28 |

Set 3: Combinations

29 | 30 |

Set 4: Common Abstraction

31 | 32 |

Set 5: Do Notation

33 | 34 | 35 | 40 | 41 |

This project is maintained by mightybyte

42 | 43 |

44 | Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. 45 |

46 | 47 |
48 |
49 |

A Missed Generalization

50 | 51 |

We just found an equivalence between generalB and yLink. In Set 2 we implemented yLink in terms of link without using any cases. But in Set 1 you might not have implemented generalB in terms of genTwo. Go back and look at your generalB implementation and if you didn’t write it in terms of genTwo, do that now and call it generalB2. Doing this should get rid of the state threading between generators.

52 |

Re-implement repRandom in terms of generalA, genTwo, and mkGen. Note that by using generalA, genTwo and mkGen you should not need to have a seed variable in your code for repRandom anywhere.

53 |

Previous Page - Next Page

54 | 55 |
56 |
57 | 58 | 62 | 68 | 69 | -------------------------------------------------------------------------------- /docs/pages/ex4-3.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | The Monad Challenges 6 | 7 | 8 | 9 | 10 | 11 | 14 | 15 | 16 |
17 |
18 |

The Monad Challenges

19 |

A set of challenges for jump starting your understanding of monads.

20 | 21 |

Outline

22 | 23 | 24 |

Set 1: Random Numbers

25 | 26 |

Set 2: Failing Computations

27 | 28 |

Set 3: Combinations

29 | 30 |

Set 4: Common Abstraction

31 | 32 |

Set 5: Do Notation

33 | 34 | 35 | 40 | 41 |

This project is maintained by mightybyte

42 | 43 |

44 | Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. 45 |

46 | 47 |
48 |
49 |

Formalizing the Pattern

50 | 51 |

Now that we have identified a pattern that we want to abstract, we need to formalize it somehow. When we did an abstraction like this before, we took a repeated type signature expression and created a shorthand name for it with a type synonym and a type variable. The modification only affected type signatures and had nothing to do with the implementations. Now we are trying to find commonalities across two implementations of completely different problems, so a simple type synonym is not going to be enough for what we want to do.

52 |

Clearly the random number example and the failing computation example have some similarities, but clearly they also have some differences. We’re trying to find the smallest set of fundamental primitives that have to be different. We have two candidates for those things: genTwo/link and generalB2/yLink. We just saw that generalB2/yLink can be written in terms of genTwo/link, so let’s assume that the genTwo/link abstraction is part of the fundamental set of primitives.

53 |

Whatever this pattern is that is common between random number generation and failing computations, we need to give it a name so we can talk about it more easily. Let’s call it a monad! You know how companies these days have been giving themselves nonsensical names that allow them to completely define their brand without competing with their customers’ preconceived notions of what common words mean? We’re doing the same thing here. (Well, mathematicians did it for us a while back.) Now that we have a name we need to create a type class:

54 |
class Monad m where
55 |

The generalized type signature for genTwo/link that you came up with in challenge #1 is one of the ones we want to put into our type class, and if you used the type variable m, you should be able to drop it in. All we need is a name. Let’s use the name bind.

56 |

Now that we have part of our type class your task is to create a single unified implementation for generalB2/yLink. Most of it should be the same, but you’ll find that there is one part that is different for the two. Make that part into the second function of the type class. Call this function return. Figure out what the type signature should be. We’ve seen this pattern before in Set 1 and Set 3.

57 |

Previous Page - Next Page

58 | 59 |
60 |
61 | 62 | 66 | 72 | 73 | -------------------------------------------------------------------------------- /docs/pages/ex4-4.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | The Monad Challenges 6 | 7 | 8 | 9 | 10 | 11 | 14 | 15 | 16 |
17 |
18 |

The Monad Challenges

19 |

A set of challenges for jump starting your understanding of monads.

20 | 21 |

Outline

22 | 23 | 24 |

Set 1: Random Numbers

25 | 26 |

Set 2: Failing Computations

27 | 28 |

Set 3: Combinations

29 | 30 |

Set 4: Common Abstraction

31 | 32 |

Set 5: Do Notation

33 | 34 | 35 | 40 | 41 |

This project is maintained by mightybyte

42 | 43 |

44 | Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. 45 |

46 | 47 |
48 |
49 |

Creating Instances

50 | 51 |

Now you have a Monad type class with two functions: bind and return. If you’ve heard anything about monads in the past, this might sound familiar. But don’t go off and look at existing monad code yet. You have a lot more ground to discover yourself.

52 |

The next thing you need to do is create instances of your Monad type class for the three types we’ve been working with: Gen, Maybe, and []. You can do this for Maybe and [] with no trouble. But Gen won’t work because it is a type synonym. First you need to replace your type synonym with a newtype. Don’t go back and modify your old code. We’ll be referring back to that in the future. Do this work in a new file Set4.hs (again using the same header we’ve been using and importing MCPrelude instead of Haskell’s Prelude). You can import Set2 because that has your Maybe data type and associated code which can be reused. But don’t import Set1. When you are finished with this challenge you should have a Set4.hs file with a Monad type class, a Gen newtype, and three instances.

53 |

Along with your Gen newtype you also might want to write a helper function to make it easier to get your random values out:

54 |
evalGen :: Gen a -> Seed -> a
55 |

If you did all the exercises properly this should be pretty straightforward. The Monad instance for Gen may be a little bit less obvious because this time you’ll have to do some newtype wrangling.

56 |

If you need to brush up your knowledge of some of these topics, check out this chapter on newtypes or this chapter on type classes in Learn You a Haskell.

57 |

If you’re having trouble with the newtype instance here’s a hex encoded code template to get you started:

58 |
6E6577747970652047656E2061203D2047656E207B2072756E47656E203A3A2053656564202D3E2028612C205365656429207DDADA696E7374616E6365204D6F6E61642047656E207768657265DA2020202072657475726E203D202E2E2EDA2020202062696E64203D202E2E2E
59 |

Again, if possible try not to use this. We’re still leaving the hard bits out. These things are worth fighting with for awhile.

60 |

Previous Page - Next Page

61 | 62 |
63 |
64 | 65 | 69 | 75 | 76 | -------------------------------------------------------------------------------- /docs/pages/ex4-5.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | The Monad Challenges 6 | 7 | 8 | 9 | 10 | 11 | 14 | 15 | 16 |
17 |
18 |

The Monad Challenges

19 |

A set of challenges for jump starting your understanding of monads.

20 | 21 |

Outline

22 | 23 | 24 |

Set 1: Random Numbers

25 | 26 |

Set 2: Failing Computations

27 | 28 |

Set 3: Combinations

29 | 30 |

Set 4: Common Abstraction

31 | 32 |

Set 5: Do Notation

33 | 34 | 35 | 40 | 41 |

This project is maintained by mightybyte

42 | 43 |

44 | Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. 45 |

46 | 47 |
48 |
49 |

Revisiting Other Generic Functions

50 | 51 |

Now that we have our monad type class and an understanding of what motivated it, go back and rewrite the generic functions from sets 1, 2, and 3 using your Monad type class. But since we’re rewriting them generically we need to give them new names. Here are the functions that you should rewrite and the new names that you should use. From set 1:

52 | 56 |

From set 2:

57 | 62 |

From set 3:

63 | 68 |

You only need to write liftM2 once. But instead of using Gen, Maybe, or [] you use m in its place with the Monad m type class constraint. Try to do this set without looking at the code you used before. Just copy all the type signatures and redo the work, but this time do everything in terms of return and bind.

69 |

Previous Page - Next Page

70 | 71 |
72 |
73 | 74 | 78 | 84 | 85 | -------------------------------------------------------------------------------- /docs/pages/ex4-6.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | The Monad Challenges 6 | 7 | 8 | 9 | 10 | 11 | 14 | 15 | 16 |
17 |
18 |

The Monad Challenges

19 |

A set of challenges for jump starting your understanding of monads.

20 | 21 |

Outline

22 | 23 | 24 |

Set 1: Random Numbers

25 | 26 |

Set 2: Failing Computations

27 | 28 |

Set 3: Combinations

29 | 30 |

Set 4: Common Abstraction

31 | 32 |

Set 5: Do Notation

33 | 34 | 35 | 40 | 41 |

This project is maintained by mightybyte

42 | 43 |

44 | Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. 45 |

46 | 47 |
48 |
49 |

Using the abstraction

50 | 51 |

Now your Set4.hs module has fairly decent set of abstract tools all built on the Monad interface. Now go back through sets 1, 2, and 3 and redo them all using the library of functions you built up in Set4.hs. Since Gen is now a newtype you will have to make some changes to the functions that use it. This may seem like a waste of time, but it will develop your familiarity with the names actually used by Haskell’s monad library. This is the core of developing a working knowledge of monads. The first three sets were the motivation. Now we’re getting to the real world use.

52 |

Previous Page - Next Page

53 | 54 |
55 |
56 | 57 | 61 | 67 | 68 | -------------------------------------------------------------------------------- /docs/pages/ex5-1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | The Monad Challenges 6 | 7 | 8 | 9 | 10 | 11 | 14 | 15 | 16 |
17 |
18 |

The Monad Challenges

19 |

A set of challenges for jump starting your understanding of monads.

20 | 21 |

Outline

22 | 23 | 24 |

Set 1: Random Numbers

25 | 26 |

Set 2: Failing Computations

27 | 28 |

Set 3: Combinations

29 | 30 |

Set 4: Common Abstraction

31 | 32 |

Set 5: Do Notation

33 | 34 | 35 | 40 | 41 |

This project is maintained by mightybyte

42 | 43 |

44 | Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. 45 |

46 | 47 |
48 |
49 |

Do Notation

50 | 51 |

Now that you’ve spent some time using the monad abstraction, you may have seen that while it does what we need, it’s a little more verbose than we would like. This is where Haskell’s do notation comes to the rescue. When you write the following do block:

52 |
rule1 = do
53 |     foo <- calcFoo
54 |     bar foo
55 |

GHC automatically desugars this to

56 |
rule1 = bind calcFoo (\foo -> bar foo)
57 |

It’s important to note what this says about the type signatures of everything involved.

58 |
rule1 :: m b
59 | calcFoo :: m a
60 | bar :: a -> m b
61 |

The key is that whatever m is, it must be the same for all three of these types. Also, calcFoo returns an m a, but bar takes a plain a. The bind function is responsible for “unboxing” the m a and passing the unboxed value to bar.

62 |

One thing that trips a lot of people up is what to do when they don’t have a function that looks like bar. The version of bar they want might instead have this type bar :: a -> b (note that here we’re specifically saying that bar’s type signature does NOT have an m). Figure out what to do in this situation.

63 |

Note this challenge isn’t concrete. We’re dealing with more abstract types that don’t have type signatures exactly the same as the ones you’ve been working with. We could have written this challenge in a concrete way, but we think it’s important to learn how to think in more abstract terms. So for this go back to the stuff you have done before and find something that you think can be written with the do block that rule1 has. Or better yet, make up a new function that does it using Maybe or Gen.

64 |

Previous Page - Next Page

65 | 66 |
67 |
68 | 69 | 73 | 79 | 80 | -------------------------------------------------------------------------------- /docs/pages/ex5-2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | The Monad Challenges 6 | 7 | 8 | 9 | 10 | 11 | 14 | 15 | 16 |
17 |
18 |

The Monad Challenges

19 |

A set of challenges for jump starting your understanding of monads.

20 | 21 |

Outline

22 | 23 | 24 |

Set 1: Random Numbers

25 | 26 |

Set 2: Failing Computations

27 | 28 |

Set 3: Combinations

29 | 30 |

Set 4: Common Abstraction

31 | 32 |

Set 5: Do Notation

33 | 34 | 35 | 40 | 41 |

This project is maintained by mightybyte

42 | 43 |

44 | Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. 45 |

46 | 47 |
48 |
49 |

Do Notation – operators

50 | 51 |

In the last exercise, we noted that the following code:

52 |
rule1 = do
53 |     foo <- calcFoo
54 |     bar foo
55 |

Is automatically de-sugared to:

56 |
rule1 = bind calcFoo (\foo -> bar foo)
57 |

In Haskell, the bind function is conventionally written as the >>= operator, which can be written infix style like this:

58 |
rule1 = calcFoo >>= (\foo -> bar foo)
59 |

In order for the do syntax to work correctly, we need to change our Monad class to have a >>= operator. Create a class like this:

60 |
class Monad m where
61 |     (>>=) :: m a -> (a -> m b) -> m b
62 |     return :: a -> m a
63 | 
64 |     fail :: String -> m a
65 |     fail = undefined
66 |

(Note: for historical reasons, Monad is required to have a fail function. We will not be concerning ourselves with failure here, so we just leave this as undefined. When you implement Monad for your own data types, you should only implement the >>= and return functions)

67 |

In the next few sections, we will be rewriting the earlier exercises using do syntax.

68 |

Previous Page - Next Page

69 | 70 |
71 |
72 | 73 | 77 | 83 | 84 | -------------------------------------------------------------------------------- /docs/pages/ex5-3.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | The Monad Challenges 6 | 7 | 8 | 9 | 10 | 11 | 14 | 15 | 16 |
17 |
18 |

The Monad Challenges

19 |

A set of challenges for jump starting your understanding of monads.

20 | 21 |

Outline

22 | 23 | 24 |

Set 1: Random Numbers

25 | 26 |

Set 2: Failing Computations

27 | 28 |

Set 3: Combinations

29 | 30 |

Set 4: Common Abstraction

31 | 32 |

Set 5: Do Notation

33 | 34 | 35 | 40 | 41 |

This project is maintained by mightybyte

42 | 43 |

44 | Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. 45 |

46 | 47 |
48 |
49 |

Do Notation – Set 1

50 | 51 |

First, create an instance of Monad for your Gen newtype, similar to what you did in Set 4. You will also probably want to create an evalGen :: Gen a -> Seed -> a function as well.

52 |

With your Monad instance, you should be able to use do syntax. Recall that in Set 1 we had a function called rand :: Seed -> (Integer, Seed). Create a new function makeRandom :: Gen Integer which wraps the rand function inside your new type.

53 |

Next, use do syntax to re-create the following function from Set 1:

54 |
fiveRands :: Gen [Integer]
55 |

To check that you created this function correctly, recall that the product of the five numbers you generate when passing in a seed of mkSeed 1 is 8681089573064486461641871805074254223660.

56 |

Once that has been created correctly, create randLetter :: Gen Char and use it to create randString3 :: Gen String, which creates a String of three random characters. If you have an initial seed of 1, when you use this site to calculate the SHA-256 hash of the output of randString3, you get 9d475eb78d3e38085220ed6ebde9d8f7d26540bb1c8f9382479c3acd4c8c94a3.

57 |

Lastly, go ahead and create this general function:

58 |
generalPair :: Gen a -> Gen b -> Gen (a, b)
59 |

Previous Page - Next Page

60 | 61 |
62 |
63 | 64 | 68 | 74 | 75 | -------------------------------------------------------------------------------- /docs/pages/ex5-4.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | The Monad Challenges 6 | 7 | 8 | 9 | 10 | 11 | 14 | 15 | 16 |
17 |
18 |

The Monad Challenges

19 |

A set of challenges for jump starting your understanding of monads.

20 | 21 |

Outline

22 | 23 | 24 |

Set 1: Random Numbers

25 | 26 |

Set 2: Failing Computations

27 | 28 |

Set 3: Combinations

29 | 30 |

Set 4: Common Abstraction

31 | 32 |

Set 5: Do Notation

33 | 34 | 35 | 40 | 41 |

This project is maintained by mightybyte

42 | 43 |

44 | Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. 45 |

46 | 47 |
48 |
49 |

Do Notation – Set 2

50 | 51 |

As with the previous set, you will want to create a Monad instance for your Maybe data type.

52 |

Once that is accomplished, rewrite the following functions using do syntax:

53 |
queryGreek :: GreekData -> String -> Maybe Double
54 | addSalaries :: [(String, Integer)] -> String -> String -> Maybe Integer
55 | tailProd :: Num a => [a] -> Maybe a
56 | tailSum :: Num a => [a] -> Maybe a
57 | tailMax :: Ord a => [a] -> Maybe a
58 |

You should import Set2 so that you can use the helper functions you wrote in that set.

59 |

You can test queryGreek with the following assertions:

60 |
queryGreek greekDataA "alpha" == Just 2.0
61 | queryGreek greekDataA "beta" == Nothing
62 | queryGreek greekDataA "gamma" == Just 3.3333333333333335
63 | queryGreek greekDataA "delta" == Nothing
64 | queryGreek greekDataA "zeta" == Nothing
65 | 
66 | queryGreek greekDataB "rho" == Nothing
67 | queryGreek greekDataB "phi" == Just 0.24528301886792453
68 | queryGreek greekDataB "chi" == Just 9.095238095238095
69 | queryGreek greekDataB "psi" == Nothing
70 | queryGreek greekDataB "omega" == Just 24.0
71 |

Previous Page - Next Page

72 | 73 |
74 |
75 | 76 | 80 | 86 | 87 | -------------------------------------------------------------------------------- /docs/pages/ex5-5.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | The Monad Challenges 6 | 7 | 8 | 9 | 10 | 11 | 14 | 15 | 16 |
17 |
18 |

The Monad Challenges

19 |

A set of challenges for jump starting your understanding of monads.

20 | 21 |

Outline

22 | 23 | 24 |

Set 1: Random Numbers

25 | 26 |

Set 2: Failing Computations

27 | 28 |

Set 3: Combinations

29 | 30 |

Set 4: Common Abstraction

31 | 32 |

Set 5: Do Notation

33 | 34 | 35 | 40 | 41 |

This project is maintained by mightybyte

42 | 43 |

44 | Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. 45 |

46 | 47 |
48 |
49 |

Do Notation – Set 3

50 | 51 |

Again, create a Monad instance for your [] type. Import your Card data constructor from Set 3. Using do syntax, implement the following functions:

52 |
allPairs :: [a] -> [b] -> [(a,b)]
53 | allCards :: [Int] -> [String] -> [Card]
54 | allCombs3 :: (a -> b -> c -> d) -> [a] -> [b] -> [c] -> [d]
55 |

Previous Page

56 | 57 |
58 |
59 | 60 | 64 | 70 | 71 | -------------------------------------------------------------------------------- /docs/pages/set1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | The Monad Challenges 6 | 7 | 8 | 9 | 10 | 11 | 14 | 15 | 16 |
17 |
18 |

The Monad Challenges

19 |

A set of challenges for jump starting your understanding of monads.

20 | 21 |

Outline

22 | 23 | 24 |

Set 1: Random Numbers

25 | 26 |

Set 2: Failing Computations

27 | 28 |

Set 3: Combinations

29 | 30 |

Set 4: Common Abstraction

31 | 32 |

Set 5: Do Notation

33 | 34 | 35 | 40 | 41 |

This project is maintained by mightybyte

42 | 43 |

44 | Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. 45 |

46 | 47 |
48 |
49 |

Set 1: Random Numbers

50 | 51 |

It may not seem like the problems in this set have much to do with monads at first, but bear with us. We are taking you through the motivations for monads in a step by step structured way. It may be tempting to skip over this stuff. DON’T DO IT. Later problem sets build on things you did here. Stick with us and try to absorb the ideas and patterns.

52 |
    53 |
  1. Random number generation
  2. 54 |
  3. Random character generation
  4. 55 |
  5. More generators
  6. 56 |
  7. Generalizing random pairs
  8. 57 |
  9. Generalizing lists of generators
  10. 58 |
  11. Threading the random number state
  12. 59 |
60 |

Next Page

61 | 62 |
63 |
64 | 65 | 69 | 75 | 76 | -------------------------------------------------------------------------------- /docs/pages/set2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | The Monad Challenges 6 | 7 | 8 | 9 | 10 | 11 | 14 | 15 | 16 |
17 |
18 |

The Monad Challenges

19 |

A set of challenges for jump starting your understanding of monads.

20 | 21 |

Outline

22 | 23 | 24 |

Set 1: Random Numbers

25 | 26 |

Set 2: Failing Computations

27 | 28 |

Set 3: Combinations

29 | 30 |

Set 4: Common Abstraction

31 | 32 |

Set 5: Do Notation

33 | 34 | 35 | 40 | 41 |

This project is maintained by mightybyte

42 | 43 |

44 | Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. 45 |

46 | 47 |
48 |
49 |

Set 2: Failing Computations

50 | 51 |

This set deals with programming patterns that arise when dealing with computations that might fail. Failing computations come up all the time in the real world and Haskell can provide great tools for dealing with them.

52 |
    53 |
  1. The Maybe type
  2. 54 |
  3. Build a library of things that can fail
  4. 55 |
  5. Chains of failing computations
  6. 56 |
  7. Generalizing chains of failures
  8. 57 |
  9. Chaining variations
  10. 58 |
  11. Tailprod
  12. 59 |
60 |

Previous Page - Next Page

61 | 62 |
63 |
64 | 65 | 69 | 75 | 76 | -------------------------------------------------------------------------------- /docs/pages/set3.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | The Monad Challenges 6 | 7 | 8 | 9 | 10 | 11 | 14 | 15 | 16 |
17 |
18 |

The Monad Challenges

19 |

A set of challenges for jump starting your understanding of monads.

20 | 21 |

Outline

22 | 23 | 24 |

Set 1: Random Numbers

25 | 26 |

Set 2: Failing Computations

27 | 28 |

Set 3: Combinations

29 | 30 |

Set 4: Common Abstraction

31 | 32 |

Set 5: Do Notation

33 | 34 | 35 | 40 | 41 |

This project is maintained by mightybyte

42 | 43 |

44 | Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. 45 |

46 | 47 |
48 |
49 |

Set 3: Combinations

50 | 51 |

In this set we are going to investigate lists. As usual, make sure you start with the standard block of header code we’ve been using for these challenges. If you don’t do this, you might inadvertently solve these problems in a way that misses the whole point of the exercise.

52 |
    53 |
  1. Generating combinations
  2. 54 |
  3. Poker hands
  4. 55 |
  5. Generalizing pairs and cards
  6. 56 |
  7. Combinations of three things
  8. 57 |
  9. Combinations of more things
  10. 58 |
59 |

Previous Page - Next Page

60 | 61 |
62 |
63 | 64 | 68 | 74 | 75 | -------------------------------------------------------------------------------- /docs/pages/set4.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | The Monad Challenges 6 | 7 | 8 | 9 | 10 | 11 | 14 | 15 | 16 |
17 |
18 |

The Monad Challenges

19 |

A set of challenges for jump starting your understanding of monads.

20 | 21 |

Outline

22 | 23 | 24 |

Set 1: Random Numbers

25 | 26 |

Set 2: Failing Computations

27 | 28 |

Set 3: Combinations

29 | 30 |

Set 4: Common Abstraction

31 | 32 |

Set 5: Do Notation

33 | 34 | 35 | 40 | 41 |

This project is maintained by mightybyte

42 | 43 |

44 | Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. 45 |

46 | 47 |
48 |
49 |

Set 4: Common Abstraction

50 | 51 |
    52 |
  1. Generalizing State and Maybe
  2. 53 |
  3. A missed generalization
  4. 54 |
  5. Formalizing the pattern
  6. 55 |
  7. Creating instances
  8. 56 |
  9. Revisiting other generic functions
  10. 57 |
  11. Using the abstraction
  12. 58 |
59 |

Previous Page - Next Page

60 | 61 |
62 |
63 | 64 | 68 | 74 | 75 | -------------------------------------------------------------------------------- /docs/pages/set5.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | The Monad Challenges 6 | 7 | 8 | 9 | 10 | 11 | 14 | 15 | 16 |
17 |
18 |

The Monad Challenges

19 |

A set of challenges for jump starting your understanding of monads.

20 | 21 |

Outline

22 | 23 | 24 |

Set 1: Random Numbers

25 | 26 |

Set 2: Failing Computations

27 | 28 |

Set 3: Combinations

29 | 30 |

Set 4: Common Abstraction

31 | 32 |

Set 5: Do Notation

33 | 34 | 35 | 40 | 41 |

This project is maintained by mightybyte

42 | 43 |

44 | Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. 45 |

46 | 47 |
48 |
49 |

Set 5: Do Notation

50 | 51 |

In this set, we are going to rewrite the challenges we completed in earlier sets using do syntax. Do syntax is a common form of syntactic sugar that you will see when writing Haskell, but it is functionally equivalent to previous functions we’ve already created.

52 |
    53 |
  1. Do notation
  2. 54 |
  3. Do notation – operators
  4. 55 |
  5. Do notation – set 1
  6. 56 |
  7. Do notation – set 2
  8. 57 |
  9. Do notation – set 3
  10. 58 |
59 |

Previous Page - Next Page

60 | 61 |
62 |
63 | 64 | 68 | 74 | 75 | -------------------------------------------------------------------------------- /docs/stylesheets/github-light.css: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014 GitHub Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | 16 | */ 17 | 18 | .pl-c /* comment */ { 19 | color: #969896; 20 | } 21 | 22 | .pl-c1 /* constant, markup.raw, meta.diff.header, meta.module-reference, meta.property-name, support, support.constant, support.variable, variable.other.constant */, 23 | .pl-s .pl-v /* string variable */ { 24 | color: #0086b3; 25 | } 26 | 27 | .pl-e /* entity */, 28 | .pl-en /* entity.name */ { 29 | color: #795da3; 30 | } 31 | 32 | .pl-s .pl-s1 /* string source */, 33 | .pl-smi /* storage.modifier.import, storage.modifier.package, storage.type.java, variable.other, variable.parameter.function */ { 34 | color: #333; 35 | } 36 | 37 | .pl-ent /* entity.name.tag */ { 38 | color: #63a35c; 39 | } 40 | 41 | .pl-k /* keyword, storage, storage.type */ { 42 | color: #a71d5d; 43 | } 44 | 45 | .pl-pds /* punctuation.definition.string, string.regexp.character-class */, 46 | .pl-s /* string */, 47 | .pl-s .pl-pse .pl-s1 /* string punctuation.section.embedded source */, 48 | .pl-sr /* string.regexp */, 49 | .pl-sr .pl-cce /* string.regexp constant.character.escape */, 50 | .pl-sr .pl-sra /* string.regexp string.regexp.arbitrary-repitition */, 51 | .pl-sr .pl-sre /* string.regexp source.ruby.embedded */ { 52 | color: #183691; 53 | } 54 | 55 | .pl-v /* variable */ { 56 | color: #ed6a43; 57 | } 58 | 59 | .pl-id /* invalid.deprecated */ { 60 | color: #b52a1d; 61 | } 62 | 63 | .pl-ii /* invalid.illegal */ { 64 | background-color: #b52a1d; 65 | color: #f8f8f8; 66 | } 67 | 68 | .pl-sr .pl-cce /* string.regexp constant.character.escape */ { 69 | color: #63a35c; 70 | font-weight: bold; 71 | } 72 | 73 | .pl-ml /* markup.list */ { 74 | color: #693a17; 75 | } 76 | 77 | .pl-mh /* markup.heading */, 78 | .pl-mh .pl-en /* markup.heading entity.name */, 79 | .pl-ms /* meta.separator */ { 80 | color: #1d3e81; 81 | font-weight: bold; 82 | } 83 | 84 | .pl-mq /* markup.quote */ { 85 | color: #008080; 86 | } 87 | 88 | .pl-mi /* markup.italic */ { 89 | color: #333; 90 | font-style: italic; 91 | } 92 | 93 | .pl-mb /* markup.bold */ { 94 | color: #333; 95 | font-weight: bold; 96 | } 97 | 98 | .pl-md /* markup.deleted, meta.diff.header.from-file */ { 99 | background-color: #ffecec; 100 | color: #bd2c00; 101 | } 102 | 103 | .pl-mi1 /* markup.inserted, meta.diff.header.to-file */ { 104 | background-color: #eaffea; 105 | color: #55a532; 106 | } 107 | 108 | .pl-mdr /* meta.diff.range */ { 109 | color: #795da3; 110 | font-weight: bold; 111 | } 112 | 113 | .pl-mo /* meta.output */ { 114 | color: #1d3e81; 115 | } 116 | 117 | -------------------------------------------------------------------------------- /monad-challenges-code/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016, Doug Beardsley 2 | 3 | All rights reserved. 4 | 5 | Haskell code in this project uses the following license: 6 | 7 | Redistribution and use in source and binary forms, with or without 8 | modification, are permitted provided that the following conditions are met: 9 | 10 | * Redistributions of source code must retain the above copyright 11 | notice, this list of conditions and the following disclaimer. 12 | 13 | * Redistributions in binary form must reproduce the above 14 | copyright notice, this list of conditions and the following 15 | disclaimer in the documentation and/or other materials provided 16 | with the distribution. 17 | 18 | * Neither the name of Doug Beardsley nor the names of other 19 | contributors may be used to endorse or promote products derived 20 | from this software without specific prior written permission. 21 | 22 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 23 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 24 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 25 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 26 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 27 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 28 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 29 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 30 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 31 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 32 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 33 | -------------------------------------------------------------------------------- /monad-challenges-code/default.nix: -------------------------------------------------------------------------------- 1 | { compiler ? "ghc881" 2 | , rev ? "c4f97342ba8ac84def72328616dd05d005bb4715" 3 | , sha256 ? "1p2gbisib2jrz4r9b5vzfvmirgmz9sr2ksalngaw908vvg9hsvai" 4 | , pkgs ? 5 | import (builtins.fetchTarball { 6 | url = "https://github.com/NixOS/nixpkgs/archive/${rev}.tar.gz"; 7 | inherit sha256; }) { 8 | config.allowBroken = false; 9 | config.allowUnfree = true; 10 | } 11 | }: 12 | let gitignoreSrc = import (pkgs.fetchFromGitHub { 13 | owner = "hercules-ci"; 14 | repo = "gitignore"; 15 | rev = "2ced4519f865341adcb143c5d668f955a2cb997f"; 16 | sha256 = "0fc5bgv9syfcblp23y05kkfnpgh3gssz6vn24frs8dzw39algk2z"; 17 | }) {}; 18 | 19 | in 20 | pkgs.haskell.packages.${compiler}.developPackage { 21 | name = builtins.baseNameOf ./.; 22 | root = gitignoreSrc.gitignoreSource ./.; 23 | 24 | overrides = self: super: with pkgs.haskell.lib; { 25 | # Don't run a package's test suite 26 | # foo = dontCheck super.foo; 27 | # 28 | # Don't enforce package's version constraints 29 | # bar = doJailbreak super.bar; 30 | # 31 | # Get a specific hackage version straight from hackage. Unlike the above 32 | # callHackage approach, this will always succeed if the version is on 33 | # hackage. The downside is that you have to specify the hash manually. 34 | # hakyll = self.callHackageDirect { 35 | # pkg = "hakyll"; 36 | # ver = "4.13.3.0"; 37 | # sha256 = "1maf8z5il6gs3gh6f9jw8pflnc50imvf8gr4x6zrfbayjnd674ql"; 38 | # } {}; 39 | 40 | # To discover more functions that can be used to modify haskell 41 | # packages, run "nix-repl", type "pkgs.haskell.lib.", then hit 42 | # to get a tab-completed list of functions. 43 | }; 44 | source-overrides = { 45 | # Use a specific hackage version using callHackage. Only works if the 46 | # version you want is in the version of all-cabal-hashes that you have. 47 | # bytestring = "0.10.8.1"; 48 | # 49 | # Use a particular commit from github 50 | # parsec = pkgs.fetchFromGitHub 51 | # { owner = "hvr"; 52 | # repo = "parsec"; 53 | # rev = "c22d391c046ef075a6c771d05c612505ec2cd0c3"; 54 | # sha256 = "0phar79fky4yzv4hq28py18i4iw779gp5n327xx76mrj7yj87id3"; 55 | # }; 56 | }; 57 | modifier = drv: pkgs.haskell.lib.overrideCabal drv (attrs: { 58 | buildTools = (attrs.buildTools or []) ++ [ 59 | pkgs.haskell.packages.${compiler}.cabal-install 60 | pkgs.haskell.packages.${compiler}.ghcid 61 | ]; 62 | }); 63 | } 64 | -------------------------------------------------------------------------------- /monad-challenges-code/hakyll/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: The Monad Challenges 3 | --- 4 | 5 |
6 | 7 | ### Work In Progress 8 | 9 | There are still some rough edges here, and also a fair amount of content to be 10 | added. But if we waited until everything was polished before publishing, it 11 | would probably never happen. So feel free to make comments, suggestions, or 12 | contribute new content. As always, pull requests are the most desirable. 13 | 14 | Bottom line: **we need more contributors**. If you would like to help, see the 15 | [contribution 16 | guide](https://github.com/mightybyte/monad-challenges/blob/gh-pages/README.md) 17 | for more information. 18 | 19 |
20 | 21 | ## Welcome 22 | 23 | The guys who wrote the [matasano crypto challenges](http://cryptopals.com/) 24 | realized that you cannot get a working knowledge of crypto without investing 25 | blood, sweat, and tears into playing with and attempting to break crypto. 26 | They even say, "But more people 'know how' to break it than can actually break 27 | it." Their crypto challenges provide a structured way for people to make the 28 | requisite blood, sweat, and tears investment to get to a place of 29 | understanding. 30 | 31 | We believe the same is true when learning monads. Monads cannot be taught. 32 | They must be _discovered_. The problem with monad tutorials is that they are 33 | predicated on someone teaching/explaining monads. But you must explore them 34 | yourself. You will never really understand monads until you do this. The 35 | problem is that beginners do not usually know how to explore. We put together 36 | this set of challenges to show you how to do that. 37 | 38 | _Please do NOT publish the answers to these challenges._ Reading answers that 39 | you did not create yourself defeats the whole purpose. 40 | 41 | ### Do I need to know category theory? 42 | 43 | No. 44 | 45 | ### Do I need to know Haskell? 46 | 47 | Yes, we assume that you have some beginner "pre-monad" level knowledge of 48 | the following Haskell concepts: 49 | 50 | * Basic syntax 51 | * Type signatures 52 | * Higher order functions 53 | * Type synonyms 54 | * newtypes 55 | * Polymorphism 56 | * Type classes 57 | 58 | We are not trying to teach you Haskell here. We are trying to guide you down the 59 | path to a fully internalized working knowledge of monads. If you need to learn 60 | Haskell, go work through some beginning Haskell materials and come back when you 61 | think you need to learn monads. 62 | 63 | ### Organizing Your Code 64 | 65 | It will probably help if you organize your code into one file per set of 66 | challenges. Some of the problem sets build on previous ones, so it will be 67 | convenient to be able to easily import your code for a whole set or find it 68 | for reference. 69 | 70 | ### Who did this? 71 | 72 | * Doug Beardsley (with feedback and suggestions from Brent Yorgey) 73 | 74 | ### Code Template 75 | 76 | For all of these problem sets you should use the following language pragmas 77 | and imports: 78 | 79 | {-# LANGUAGE MonadComprehensions #-} 80 | {-# LANGUAGE RebindableSyntax #-} 81 | 82 | module Set1 where 83 | 84 | import MCPrelude 85 | 86 | MCPrelude should be the only module you import. Do not import any other 87 | modules. We are giving you a special prelude because we want you to solve 88 | these challenges without the use of monad libraries that have already done the 89 | work for you. Also, don't look at the code in Control.Monad, the transformers 90 | package, or the mtl package. You're doing this to learn. To do that you need 91 | to struggle with the concepts. Don't cheat yourself out of the benefit you 92 | can get by working through things yourself. 93 | 94 | Many of the problem sets build on things you did in previous sets, so you may 95 | want to put all your code for each set into a single module to make it easier 96 | to reuse your previous work. In later sets you will probably want to go back 97 | and look at your code for each set to find similarities. 98 | 99 | ### Getting Started 100 | 101 | First clone the monad-challenges github repository and build the associated 102 | library. 103 | 104 | git clone https://github.com/mightybyte/monad-challenges.git 105 | cd monad-challenges/monad-challenges-code 106 | cabal install 107 | 108 | This makes the MCPrelude module available on your system. Now head on over to 109 | [Set 1](pages/set1.html) and start coding! 110 | 111 | Once you've written some code, the easiest way to test it is to use Haskell's 112 | REPL, `ghci`. 113 | 114 | ghci Set1.hs 115 | 116 | You can inspect symbols you've written just by typing them in `ghci`. 117 | 118 | $ ghci Set1.hs 119 | GHCi, version 7.10.2: http://www.haskell.org/ghc/ :? for help 120 | [1 of 1] Compiling Set1 ( Set1.hs, interpreted ) 121 | Ok, modules loaded: Set1. 122 | *Set1> fiveRands 123 | [33614,564950498,1097816499,1969887316,140734213] 124 | *Set1> 125 | 126 | You can also type `:r` to reload Set1.hs after you make code changes. 127 | (Note that the above numbers are not the correct answer for fiveRands.) 128 | 129 | ### Hex Decoding 130 | 131 | Some challenges include hex encoded hints if you get stuck. You can put "hex 132 | decoder" into your favorite search engine to find numerous web pages that will 133 | do the decoding for you. Or you can use this bit of Haskell: 134 | 135 | import Numeric (readHex) 136 | import Data.List.Split (chunksOf) 137 | 138 | hexDecode :: String -> String 139 | hexDecode = map (toEnum . fst . head . readHex) . (chunksOf 2) 140 | -------------------------------------------------------------------------------- /monad-challenges-code/hakyll/pages/ex1-1.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Random Number Generation 3 | --- 4 | 5 | In MCPrelude we provide a simple random number generation function 6 | `rand`. Random number generators usually rely on mutable state side effects. 7 | They maintain some state somewhere in memory and use that to figure out what 8 | "random" number to give you. Before the function returns, it modifies the 9 | mutable state so that the next time you call the function you'll get a 10 | different number. Haskell is a pure functional programming language and 11 | because our custom Prelude hides Haskell's mechanisms for dealing with side 12 | effects, we can't build a random number generator that way. Our random number 13 | generator has to have everything it needs passed in and it has to return 14 | everything it modifies. Therefore, it has this type signature: 15 | 16 | rand :: Seed -> (Integer, Seed) 17 | 18 | You can construct seeds with the `mkSeed` function. 19 | 20 | mkSeed :: Integer -> Seed 21 | 22 | Make a function that gives you the first five random numbers starting with a 23 | seed of `(mkSeed 1)`. Call it: 24 | 25 | fiveRands :: [Integer] 26 | 27 | For now don't try to do anything fancy. Just implement it in the most 28 | straightforward way that comes to mind. To check your answers, the product of 29 | these numbers is 8681089573064486461641871805074254223660. 30 | 31 | [Previous Page](set1.html) - [Next Page](ex1-2.html) 32 | -------------------------------------------------------------------------------- /monad-challenges-code/hakyll/pages/ex1-2.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Random Character Generation 3 | --- 4 | 5 | Now use the `rand` function to make your own function for generating random 6 | letters of the alphabet. It should only generate lower case letters a-z. 7 | 8 | We supply a function in MCPrelude called `toLetter`. Use the `toLetter` function 9 | to write this random letter function: 10 | 11 | randLetter :: Seed -> (Char, Seed) 12 | 13 | Now use this function to write another function `randString3` that generates a 14 | random string of three letters using an initial seed of 1. 15 | 16 | randString3 :: String 17 | 18 | When you use [this site](http://www.xorbin.com/tools/sha256-hash-calculator) to 19 | calculate the SHA-256 hash of the output of `randString3`, you get 20 | 9d475eb78d3e38085220ed6ebde9d8f7d26540bb1c8f9382479c3acd4c8c94a3. 21 | 22 | [Previous Page](ex1-1.html) - [Next Page](ex1-3.html) 23 | -------------------------------------------------------------------------------- /monad-challenges-code/hakyll/pages/ex1-3.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: More Generators 3 | --- 4 | 5 | Now write a few more functions to generate different subsets of random 6 | numbers. But first, we need to generalize a bit. If we look at the type 7 | signatures for `rand` and `randLetter`, what are the commonalities? 8 | 9 | rand :: Seed -> (Integer, Seed) 10 | randLetter :: Seed -> (Char, Seed) 11 | 12 | This common pattern can be captured in a type synonym. The key point here is that 13 | they use two different types, so you will have to introduce a type parameter. 14 | Create a type synonym that would allow us to change the type signatures of these 15 | two functions to the following: 16 | 17 | rand :: Gen Integer 18 | randLetter :: Gen Char 19 | 20 | This is a relatively obvious type synonym and it is pretty easy to write, but it 21 | enables a big mental leap. Now that we have this type synonym we've jumped up a 22 | level of abstraction and higher level patterns will become more apparent. First 23 | go back and rewrite all your existing type signatures using this `Gen` type 24 | synonym. Now write three new functions: 25 | 26 | randEven :: Gen Integer -- the output of rand * 2 27 | randOdd :: Gen Integer -- the output of rand * 2 + 1 28 | randTen :: Gen Integer -- the output of rand * 10 29 | 30 | Where `randEven` and `randOdd` only generate even and odd numbers respectively, 31 | `randTen` only generates multiples of 10. Write `randEven` in terms of `rand`, 32 | write `randOdd` in terms of `randEven`, and write `randTen` however you want. 33 | There's a general pattern lurking here. If you implement all these functions 34 | the naive way like you did `fiveRands`, you'll be repeating the same pattern 35 | over and over. Figure out how to exploit the common pattern and write a 36 | function called `generalA` that implements this pattern. All three of the above 37 | functions will also use `generalA`. There are a number of different ways you 38 | could abstract this. We realize that you may not pick the right abstraction. 39 | Play around with as many different possibilities as you can think of. 40 | 41 | SPEND SOME TIME WITH THIS BEFORE READING THE HINT! 42 | 43 | Play with a number of abstractions and try to find the most flexible one. After 44 | you've spent some time with it, look at the hint [here](ex1-3hint.html). 45 | 46 | Pass all three of these functions a seed of 1. They'll give you three numbers 47 | back, the product of which is 189908109902700. 48 | 49 | [Previous Page](ex1-2.html) - [Next Page](ex1-4.html) 50 | -------------------------------------------------------------------------------- /monad-challenges-code/hakyll/pages/ex1-3hint.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: First Generalization Hint 3 | --- 4 | 5 | All three of these functions are conceptually a call to `rand` followed by 6 | applying a function to the Integer it returns. What you ultimately need is a 7 | function that you can pass a `Gen a` to and will transform the result. 8 | 9 | Be careful not to make your function too specific to `rand`. `rand` is a specific example of a `Gen Integer` but you want your function to work for other kinds of `Gen a` as well. 10 | 11 | If you are still stuck, you can view the expected type signature by hex decoding the following string: 67656E6572616C41203A3A202861202D3E206229202D3E2047656E2061202D3E2047656E2062 12 | 13 | You can decode it with [this online hex decoder](http://www.convertstring.com/EncodeDecode/HexDecode). 14 | 15 | [Back](ex1-3.html) 16 | -------------------------------------------------------------------------------- /monad-challenges-code/hakyll/pages/ex1-4.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Generalizing Random Pairs 3 | --- 4 | 5 | Use the provided `rand` function and your `randLetter` function to make a function 6 | that generates a random pair of a Char and an Integer. 7 | 8 | randPair :: Gen (Char, Integer) 9 | 10 | Generate the letter first, then generate the number using the seed acquired from 11 | generating the letter. When you give this function `(mkSeed 1)` it gives you the 12 | random pair `('l',282475249)`. 13 | 14 | Here we're starting to see the `Gen` type synonym pay off. Without `Gen`, the type 15 | would have been: 16 | 17 | randPair :: Seed -> ((Char, Integer), Seed) 18 | 19 | Still understandable, but the nested tuples are starting to obscure things 20 | slightly. 21 | 22 | You have just composed two generators. Now generalize the composition to 23 | `generalPair`, and make sure that the second argument receive the seed 24 | generated by the first argument. 25 | 26 | generalPair :: Gen a -> Gen b -> Gen (a,b) 27 | 28 | This function makes the `Gen` type synonym almost essential. Here's what we would 29 | have had to write if we didn't have `Gen`: 30 | 31 | generalPair :: (Seed -> (a, Seed)) -> (Seed -> (b, Seed)) -> (Seed -> ((a,b), Seed)) 32 | 33 | Removing the unnecessary parentheses gets us this type signature, which might 34 | help you a little when implementing the function. 35 | 36 | generalPair :: (Seed -> (a, Seed)) -> (Seed -> (b, Seed)) -> Seed -> ((a,b), Seed) 37 | 38 | Write another version `randPair_` using `generalPair`, and test it by comparing 39 | its output to what you got from `randPair`. 40 | 41 | ## Generalizing Pairs Even More 42 | 43 | This `generalPair` function can be generalized even more. Instead of always 44 | constructing pairs, you should be able to have a generalization that can 45 | construct anything. Your result shouldn't be fixed to `Gen (a,b)`. It should 46 | also be able to be `Gen String`, `Gen Polynomial`, or `Gen BlogPost`. All you 47 | need to do is pass in a function that does the constructing with two inputs. 48 | Call this even more generalized function `generalB`. Once you have it implemented, 49 | write a new `generalPair2` function in terms of `generalB`. 50 | 51 | [Previous Page](ex1-3.html) - [Next Page](ex1-5.html) 52 | -------------------------------------------------------------------------------- /monad-challenges-code/hakyll/pages/ex1-5.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Generalizing Lists of Generators 3 | --- 4 | 5 | By now you have probably realized that generating multiple random numbers this 6 | way is rather painful. You have to thread the output seed from one `rand` call 7 | to the input of the next call. This is tedious and error prone, so now you 8 | will create a function to make this a little easier. 9 | 10 | repRandom :: [Gen a] -> Gen [a] 11 | 12 | This function lets you give it a list of generators and it automatically 13 | handles the state threading for you. 14 | 15 | The nice thing about this function is that `[Gen a]` is really general, so it 16 | composes well with other built-in list functions. For example: 17 | 18 | repRandom (replicate 3 randLetter) (mkSeed 1) 19 | 20 | This function should generate the same three letters that you got from 21 | `randString3` in challenge #2. 22 | 23 | [Previous Page](ex1-4.html) - [Next Page](ex1-6.html) 24 | -------------------------------------------------------------------------------- /monad-challenges-code/hakyll/pages/ex1-6.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Threading the random number state 3 | --- 4 | 5 | In the previous exercise we wrote something that handled the threading of state 6 | through a list of generators. A simpler idea is to have a function that does 7 | one step of two generators and the necessary state threading. Now write a 8 | function called `genTwo` that does this. Its first argument will be a 9 | generator. Its second argument will be a function that takes the result of 10 | the first generator and returns a second generator. The type signature looks 11 | like this: 12 | 13 | genTwo :: Gen a -> (a -> Gen b) -> Gen b 14 | 15 | Implement this function. 16 | 17 | Now look at the implementation of your `repRandom` function. It probably has one 18 | clause handling the empty list case. That case probably looks something like this: 19 | 20 | repRandom [] s = ([], s) 21 | 22 | `repRandom` was expecting a list of generators and it's supposed to return a 23 | generator. In the empty list case it has no incoming generators to work with 24 | but it still has to return one. Essentially what's happening here is that it 25 | has to construct a `Gen` out of thin air. It turns out that this is a really 26 | common pattern. So let's make a function for it. We'll call this function 27 | `mkGen`. It has to return a `Gen a`. But it has to get the a from somewhere, so 28 | that will have to be the argument. 29 | 30 | Implement `mkGen`. Try to figure out the type signature yourself, but if you 31 | need help here it is hex-encoded: 6D6B47656E203A3A2061202D3E2047656E2061. You 32 | can decode it with [this online hex 33 | decoder](http://www.convertstring.com/EncodeDecode/HexDecode). 34 | 35 | Congrats! You have finished the first set. There are a few repeating patterns 36 | lurking around. Lets see more examples in the following two sets, and we will 37 | provide a unified approach in Set 4. Don't jump ahead! We have not seen all of 38 | them! 39 | 40 | [Previous Page](ex1-5.html) - [Next Page](set2.html) 41 | -------------------------------------------------------------------------------- /monad-challenges-code/hakyll/pages/ex2-1.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: The Maybe Type 3 | --- 4 | 5 | You may have noticed that MCPrelude doesn't have the `Maybe` type or anything 6 | from Data.Maybe. That's because you're going to build it all yourself. 7 | 8 | **IMPORTANT** 9 | Again, it is imperative that you DO NOT CHEAT. Don't look at any of the `Maybe` 10 | stuff from Prelude or Data.Maybe. Don't do it. Nobody is forcing you to do 11 | these exercises, so you should try to get the maximum possible benefit. 12 | **IMPORTANT** 13 | 14 | First of all, you need to define the `Maybe` type. It should be able to 15 | represent any value `a`, as well as the case where no value `a` exists. This 16 | type needs to represent failing values of any type, so it needs a type variable 17 | similar to what we saw in the `Gen` type synonym. But this can't be a type 18 | synonym because it has two constructors. Write this type yourself and get it to 19 | compile. Once you've gotten it compiling, check your answer by [hex 20 | decoding](http://www.convertstring.com/EncodeDecode/HexDecode) the following: 21 | 22 | 64617461204D617962652061203D204E6F7468696E67207C204A7573742061 23 | 24 | You should use this definition and names going forward. We just wanted you to 25 | work on it yourself first. 26 | 27 | Then for convenience write a `Show` instance for this new type. 28 | 29 | instance Show a => Show (Maybe a) where 30 | -- show :: Maybe a -> String 31 | show = undefined 32 | 33 | If you're having trouble with this, here is the [hex 34 | encoded](http://www.convertstring.com/EncodeDecode/HexDecode) instance. 35 | 36 | 202073686F77204E6F7468696E67203D20224E6F7468696E6722DA202073686F7720284A757374206129203D20224A7573742022202B2B2073686F772061 37 | 38 | You are also going to need an `Eq` instance as well. 39 | 40 | instance Eq a => Eq (Maybe a) where 41 | -- (==) :: Maybe a -> Maybe a -> Bool 42 | (==) = undefined 43 | 44 | We're not going to give you the answer to this one. In the worst case 45 | scenario you should be able to figure it out from the way the `Show` instance 46 | was done. 47 | 48 | On the other hand, if writing these instances was too difficult, it might be 49 | good to go study some more introductory Haskell materials before continuing 50 | here. 51 | 52 | [Previous Page](set2.html) - [Next Page](ex2-2.html) 53 | -------------------------------------------------------------------------------- /monad-challenges-code/hakyll/pages/ex2-2.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Build a library of things that can fail 3 | --- 4 | 5 | Now that you have a `Maybe` type, you need to use it. So now use it to build 6 | safe versions of several common functions specified by the Prelude. 7 | 8 | headMay :: [a] -> Maybe a 9 | tailMay :: [a] -> Maybe [a] 10 | lookupMay :: Eq a => a -> [(a, b)] -> Maybe b 11 | divMay :: (Eq a, Fractional a) => a -> a -> Maybe a 12 | maximumMay :: Ord a => [a] -> Maybe a 13 | minimumMay :: Ord a => [a] -> Maybe a 14 | 15 | The functions `headMay` and `tailMay` are "safe" versions of the well known `head` and 16 | `tail` functions. The former returns the first element of a list or `Nothing` if the 17 | list is empty. The latter returns a list containing all but the first element of 18 | a list, or `Nothing` if the list is empty. The `lookupMay` function is kind of like 19 | the `lookup` for a map. Find the first tuple in the list where the first element 20 | is the equal to the passed in value and return the second element. If there is 21 | no matching `a`, then return `Nothing`. The `divMay` function should return `Nothing` 22 | if you're dividing by `0` and the result of the division otherwise. `maximumMay` 23 | and `minimumMay` calculate the maximum and minimum respectively of all the numbers 24 | in the list, but if the list is empty they return `Nothing`. 25 | 26 | [Previous Page](ex2-1.html) - [Next Page](ex2-3.html) 27 | -------------------------------------------------------------------------------- /monad-challenges-code/hakyll/pages/ex2-3.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Chains of Failing Computations 3 | --- 4 | 5 | In MCPrelude we have defined the following type synonym for you. 6 | 7 | type GreekData = [(String, [Integer])] 8 | 9 | We also provide two data structures `greekDataA, greekDataB :: GreekData` that 10 | have some sample data. 11 | 12 | Write a function to query the above data structures. Your function should have 13 | the following type signature: 14 | 15 | queryGreek :: GreekData -> String -> Maybe Double 16 | 17 | Your implementation of this function should use the functions you wrote in the 18 | previous exercise to do the following: first query the `GreekData` that is passed 19 | in, look up the string passed in the second argument, and retrieve the 20 | corresponding list of Integers. Call this list xs. Next calculate the maximum of 21 | the tail of xs. (Don't use any pattern matching here. Use case expressions and 22 | the `maximumMay` and `tailMay` functions you wrote in the last exercise.) Take the 23 | maximum and divide it by the head of the list (using your `headMay` and `divMay` 24 | functions). If any of these operations along the way return `Nothing`, then your 25 | function should return `Nothing`. But if everything succeeds, then return the 26 | final quotient. One hint... you'll need to use the `fromIntegral` function to 27 | convert your two Integers to Doubles for the final call to `divMay`. 28 | 29 | You will probably find this function pretty annoying to implement. Stick with 30 | us though... there is a point. The more you feel the pain now, the more the 31 | solutions will stick in your head later. Ultimately, we will get rid of the 32 | repeating part. But don't jump ahead! We haven't seen all patterns. 33 | 34 | Your function should generate the following results: 35 | 36 | queryGreek greekDataA "alpha" == Just 2.0 37 | queryGreek greekDataA "beta" == Nothing 38 | queryGreek greekDataA "gamma" == Just 3.3333333333333335 39 | queryGreek greekDataA "delta" == Nothing 40 | queryGreek greekDataA "zeta" == Nothing 41 | 42 | queryGreek greekDataB "rho" == Nothing 43 | queryGreek greekDataB "phi" == Just 0.24528301886792453 44 | queryGreek greekDataB "chi" == Just 9.095238095238095 45 | queryGreek greekDataB "psi" == Nothing 46 | queryGreek greekDataB "omega" == Just 24.0 47 | 48 | If your function threw any kind of exception on any of those inputs, then your 49 | implementation is wrong. Make sure your function always returns a `Nothing` or 50 | a `Just` in every case. 51 | 52 | [Previous Page](ex2-2.html) - [Next Page](ex2-4.html) 53 | -------------------------------------------------------------------------------- /monad-challenges-code/hakyll/pages/ex2-4.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Generalizing chains of failures 3 | --- 4 | 5 | If you solved the last challenge correctly, your function has no fewer than three 6 | case expressions. There are four points where the computation can fail and you 7 | need to check all of them, but two of them fail or succeed together. It might 8 | bring to mind null checks in C. The difference is that in C the checks are 9 | optional because null is a valid pointer value. So in Haskell it actually looks 10 | worse than in C because Haskell forces you to check everything. (There are ways 11 | around it, but we don't want you to use those right now. And those methods are 12 | widely considered unsafe anyway.) 13 | 14 | Bottom line...this is obviously not how we want to write our code. There is a 15 | huge amount of repetition in our `queryGreek` function and we need to figure out how 16 | to get rid of it. If you're really ambitious, stop reading now and see if you 17 | can figure it out. If you can't figure it out don't worry, keep reading. 18 | 19 | For a clue, let's take another look at some of the type signatures we're 20 | working with (removing some type class constraints for clarity). 21 | 22 | headMay :: [a] -> Maybe a 23 | tailMay :: [a] -> Maybe [a] 24 | maximumMay :: [a] -> Maybe a 25 | 26 | All of these functions look very similar. What is the pattern that they all fit 27 | into? Well, they all return a `Maybe something`. And their parameter is always 28 | something else that is not a `Maybe`. So how would we generalize this pattern? The 29 | standard trick to generalizing things is to stick type variables in place of all 30 | the things that can change. The pattern here looks like this: 31 | 32 | :: a -> Maybe b 33 | 34 | Now let's see how the other functions fit into this pattern. First let's look 35 | at `lookupMay`. 36 | 37 | lookupMay :: a -> [(a,b)] -> Maybe b 38 | 39 | We could flip the argument order around and supply a fixed list of pairs. 40 | 41 | flip lookupMay [] :: a -> Maybe b 42 | 43 | Bingo, this is exactly the same pattern, so maybe we're on to something. If 44 | all of these functions fit into this pattern, how can we remove the 45 | redundancy? Well, the problem is that the thing we are always passing to 46 | these functions is a `Maybe`. But the functions need something that is not a 47 | `Maybe`. It sounds like we need a linking function that does this for us. But 48 | what will this function look like? Well, the first thing that it needs is a 49 | function that fits the above pattern. So let's start out with a partial type 50 | signature. 51 | 52 | chain :: (a -> Maybe b) -> ... 53 | 54 | Ok, now our chain function has the function that it needs to pass something to. 55 | But what does it need next? Well, there are two ways to think about this. 56 | One way is to think of `chain` as a function that takes one function and 57 | transforms it into another function. Our problem in `queryGreek` was that we always 58 | had a `Maybe a` instead of an `a`. So maybe that suggests what the rest of 59 | this function should be... 60 | 61 | chain :: (a -> Maybe b) -> (Maybe a -> Maybe b) 62 | 63 | The other way of thinking about it is what kind of data we had to work with. 64 | At every step of the way in `queryGreek` we had a `Maybe a`. So maybe the next 65 | argument to `chain` should be that. 66 | 67 | chain :: (a -> Maybe b) -> Maybe a -> ... 68 | 69 | When we think of it this way, we can view `chain` as a function that strips off 70 | the `Maybe` from the `a` and passes it to the function. If it does that, then 71 | what will the return type be? Well, it will be whatever the first function 72 | returned...in this case a `Maybe b`. 73 | 74 | chain :: (a -> Maybe b) -> Maybe a -> Maybe b 75 | 76 | If you know the associativity of -> you'll know that this is exactly the same 77 | as the first type signature we had for `chain` (minus a set of parenthesis). 78 | 79 | With that long winded explanation, we get to your task for this challenge. 80 | Implement the function `chain`. Then implement one more function that is the 81 | flipped version of `chain`: 82 | 83 | link :: Maybe a -> (a -> Maybe b) -> Maybe b 84 | 85 | After you do that, implement this function using your `link` function. (You can 86 | also do it with `chain`, but `link` tends to facilitate a more convenient style.) 87 | 88 | queryGreek2 :: GreekData -> String -> Maybe Double 89 | 90 | This function should have the exact same behavior as `queryGreek` from the 91 | previous exercise. 92 | 93 | Writing `queryGreek2` will probably be more difficult than writing `chain`. There 94 | should be no case expressions in `queryGreek2`--only calls to `link` or `chain`. 95 | Once you have it working, play around with other syntax possibilities and see 96 | if you can get it to look nice. Hint: lambdas are your friend. 97 | 98 | Don't forget to check `queryGreek2` by running testing cases similar to that in 99 | the previous page. 100 | 101 | [Previous Page](ex2-3.html) - [Next Page](ex2-5.html) 102 | -------------------------------------------------------------------------------- /monad-challenges-code/hakyll/pages/ex2-5.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Chaining variations 3 | --- 4 | 5 | Our division function returned a `Maybe` because there is a case when it can't 6 | give a meaningful answer. But what if we were using a different operator that 7 | doesn't fail? 8 | 9 | salaries :: [(String, Integer)] 10 | salaries = [ ("alice", 105000) 11 | , ("bob", 90000) 12 | , ("carol", 85000) 13 | ] 14 | 15 | Write a function `addSalaries` that adds two salaries by person name. 16 | 17 | addSalaries :: [(String, Integer)] -> String -> String -> Maybe Integer 18 | 19 | You give this function a data structure with salary info and the names of two 20 | people and it returns the sum of their salaries. But if you give it a name 21 | that is not in the list, then there is no way to add the salaries so it should 22 | return `Nothing`. 23 | 24 | Now generalize this pattern in the same way we generalized `chain`/`link` in 25 | the previous exercise. 26 | 27 | Call this function `yLink`...because it is kind of like a y-shaped `chain`/`link`. 28 | Coming up with the right type signature is often the tricky part. Take a look 29 | at how we came up with the `chain` type signature in the last exercise and see 30 | if you can came up with `yLink`'s type signature. Make a serious effort here. 31 | 32 | If you were not able to come up with a good generalized type signature, here is 33 | the [hex](http://www.convertstring.com/EncodeDecode/HexDecode) encoded version 34 | of what we are looking for. 35 | 36 | 794C696E6B203A3A202861202D3E2062202D3E206329202D3E204D617962652061202D3E204D617962652062202D3E204D617962652063 37 | 38 | Once you have the type signature, implement `yLink` using `link`. *Do not use cases or pattern matching*. Once 39 | you have that working, implement `addSalaries` again as `addSalaries2` using `yLink` 40 | this time. 41 | 42 | Notice that in both `addSalaries` and `yLink` you have to construct a `Maybe` out of 43 | thin air kind of like we did in Set 1 with `mkGen`. Write a similar function for 44 | `Maybe`s with this type signature. 45 | 46 | mkMaybe :: a -> Maybe a 47 | 48 | Now use this function in your `addSalaries` and `yLink` functions. You'll probably 49 | find this easier for `Maybe` than it was for `Gen`. It may not seem worthwhile, but 50 | this is all about finding common patterns. This pattern has come up twice in two 51 | different contexts now, so it's probably worth paying attention to. 52 | 53 | [Previous Page](ex2-4.html) - [Next Page](ex2-6.html) 54 | -------------------------------------------------------------------------------- /monad-challenges-code/hakyll/pages/ex2-6.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Tailprod 3 | --- 4 | 5 | Write a function that calculates the product of the tail of a list. Use your 6 | `tailMay` function and the `product` function defined in the Prelude. This 7 | function should return `Nothing` when passed an empty list, `Just 1` when 8 | passed a list of one element (which is what the Prelude's `product` function 9 | does), and return the product of the tail for larger lists. 10 | 11 | tailProd :: Num a => [a] -> Maybe a 12 | 13 | Now write a similar but slightly different function: 14 | 15 | tailSum :: Num a => [a] -> Maybe a 16 | 17 | These two functions have a lot in common. See if you can abstract out the 18 | commonality. To do this, write another function called `transMaybe` that is 19 | the generalized version of both of these. Spend some time working on this 20 | before you look at the next [hex 21 | encoded](http://www.convertstring.com/EncodeDecode/HexDecode) hint: 22 | 23 | 7472616E734D61796265203A3A202861202D3E206229202D3E204D617962652061202D3E204D617962652062 24 | 25 | Write a function with that type signature and then go back and implement 26 | `tailProd` and `tailSum` in terms of that function. 27 | 28 | Now that you have that finished, use `transMaybe` again, with `maximumMay` and 29 | `minimumMay`, to write two more functions `tailMax` and `tailMin`. These 30 | functions will have different type signatures than `tailProd` and `tailSum`. 31 | See if you can figure out what they should be. If you can't figure it out, 32 | here's a [hex](http://www.convertstring.com/EncodeDecode/HexDecode) encoded 33 | hint. 34 | 35 | 7461696C4D6178203A3A204F72642061203D3E205B615D202D3E204D6179626520284D61796265206129 36 | 37 | That type signature is different from the ones above for `tailProd` and 38 | `tailSum` and less convenient to use. To collapse the two levels you'll need 39 | another helper function. See if you can figure out what the type signature of 40 | this function should be and how to implement it. Call this function `combine`. 41 | Here is its [hex](http://www.convertstring.com/EncodeDecode/HexDecode) encoded 42 | type signature if you need help. 43 | 44 | 636F6D62696E65203A3A204D6179626520284D61796265206129202D3E204D617962652061 45 | 46 | [Previous Page](ex2-5.html) - [Next Page](set3.html) 47 | -------------------------------------------------------------------------------- /monad-challenges-code/hakyll/pages/ex3-1.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Generating combinations 3 | --- 4 | 5 | Have you ever needed to generate the Cartesian product of some things? Let's 6 | use the term combination to mean an element from the Cartesian product. In 7 | imperative languages generating combinations always seemed like a pain to me. 8 | We are going to explore how Haskell can make combination generation easier. 9 | 10 | First, write this function: 11 | 12 | allPairs :: [a] -> [b] -> [(a,b)] 13 | 14 | This function should generate all possible pairs of items from each of the two 15 | input lists. This means it should NOT have this behavior: 16 | 17 | allPairs [1,2,3] [4,5,6] == [(1,4),(2,5),(3,6)] 18 | 19 | That's the `zip` function and it's not what we are looking for here. Instead, 20 | your function should generate this: 21 | 22 | allPairs [1,2] [3,4] == [(1,3),(1,4),(2,3),(2,4)] 23 | 24 | Here is another test case: 25 | 26 | allPairs [1..3] [6..8] == [(1,6),(1,7),(1,8),(2,6),(2,7),(2,8),(3,6),(3,7),(3,8)] 27 | 28 | Note that because of the way we set up the project template you cannot use list 29 | comprehensions to solve this. That's intentional. You should solve it with 30 | explicit recursion. 31 | 32 | [Previous Page](set2.html) - [Next Page](ex3-2.html) 33 | -------------------------------------------------------------------------------- /monad-challenges-code/hakyll/pages/ex3-2.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Poker hands 3 | --- 4 | 5 | We can use the `allPairs` function to do things like generate poker hands. In 6 | MCPrelude we have defined two lists called `cardRanks` and `cardSuits`. Try 7 | calling your `allPairs` function on these: 8 | 9 | allPairs cardRanks cardSuits == [(2,"H"),(2,"D"),(2,"C"),(2,"S"),(3,"H"),(3,"D"),(3,"C"),(3,"S"),(4,"H"),(4,"D"),(4,"C"),(4,"S"),(5,"H"),(5,"D"),(5,"C"),(5,"S")] 10 | 11 | But this isn't a very nice representation. We want a more concise 12 | representation of the card to show our user. If you were writing a real 13 | poker-related program, instead of using a tuple you would probably create a 14 | data type `Card`. Do that now and then write a `Show` instance for it that 15 | returns the more concise representation `"2H"`, `"2D"`, etc. 16 | 17 | show (Card 2 "h") == "2h" 18 | 19 | Now create a new function `allCards` that does the same thing as your `allPairs` 20 | function but uses your new `Card` data type instead. It should have the 21 | following type signature: 22 | 23 | allCards :: [Int] -> [String] -> [Card] 24 | 25 | This function should do the same thing as `allPairs`, but with more concise 26 | output. When you write this function, don't implement it using your previous 27 | `allPairs` function. Rewrite it. 28 | 29 | show (allCards cardRanks cardSuits) == "[2H,2D,2C,2S,3H,3D,3C,3S,4H,4D,4C,4S,5H,5D,5C,5S]" 30 | 31 | [Previous Page](ex3-1.html) - [Next Page](ex3-3.html) 32 | -------------------------------------------------------------------------------- /monad-challenges-code/hakyll/pages/ex3-3.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Generalizing pairs and cards 3 | --- 4 | 5 | As we have done before, look at your `allPairs` and `allCards` functions and find 6 | the differences. Then implement a more general function that can be used to 7 | implement both `allPairs` and `allCards`. Call this new function `allCombs`. 8 | 9 | allCombs :: (a -> b -> c) -> [a] -> [b] -> [c] 10 | 11 | Then go back and reimplement `allPairs` and `allCards` in terms of `allCombs`. 12 | Verify that they do the same thing as the original functions. 13 | 14 | [Previous Page](ex3-2.html) - [Next Page](ex3-4.html) 15 | -------------------------------------------------------------------------------- /monad-challenges-code/hakyll/pages/ex3-4.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Combinations of three things 3 | --- 4 | 5 | Our `allCombs` function is more general but it still only lets us generate 6 | combinations of two things. Now implement an `allCombs3` function that generates 7 | combinations of 3 things. Don't try to do anything fancy yet. Just use the most 8 | straightforward approach you can think of. 9 | 10 | allCombs3 :: (a -> b -> c -> d) -> [a] -> [b] -> [c] -> [d] 11 | 12 | Here is example output to check your function. 13 | 14 | allCombs3 (,,) [1,2] [3,4] [5,6] == [(1,3,5),(1,3,6),(1,4,5),(1,4,6),(2,3,5),(2,3,6),(2,4,5),(2,4,6)] 15 | 16 | [Previous Page](ex3-3.html) - [Next Page](ex3-5.html) 17 | -------------------------------------------------------------------------------- /monad-challenges-code/hakyll/pages/ex3-5.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Combinations of more things 3 | --- 4 | 5 | You probably noticed that `allCombs3` is getting painful to write. Worse still, 6 | it does not help us generate combinations of four or more things. We need to 7 | take a different approach if we want this to scale easily. This is a 8 | difficult step, so we are not going to make you figure it out yourself. All 9 | the same, spend some time thinking about how you might do it. Here is a hint 10 | though. You probably noticed that `allCombs` has `([a] -> [b])` in its type 11 | signature and `allCombs3` has `([a] -> [b] -> [c])` in its type signature. When 12 | you are playing with this, do not try to generalize that pattern to `[[a]]`. 13 | That is not an adequate generalization because it only allows `a` and has no `b` 14 | or `c` anywhere. Spend some time thinking about this before you continue. But 15 | don't be discouraged if you can't figure it out. This one is hard. 16 | 17 | Back? If you figured it out, congratulations. If not, here is the 18 | [hex](http://www.convertstring.com/EncodeDecode/HexDecode) encoded type 19 | signature for a function called `combStep` that is the generalization we need. 20 | 21 | 636F6D6253746570203A3A205B61202D3E20625D202D3E205B615D202D3E205B625D 22 | 23 | Now write that function. Then use the `combStep` function to implement `allCombs` 24 | and `allCombs3` and check that the new implementations have the same behavior as 25 | before. Once you do this it should be pretty obvious how you would use `combStep` 26 | to implement `allCombs4` and beyond. Also notice how `combStep` compares to 27 | `allCombs`. 28 | 29 | 30 | 31 | [Previous Page](ex3-4.html) - [Next Page](set4.html) 32 | -------------------------------------------------------------------------------- /monad-challenges-code/hakyll/pages/ex4-1.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Generalizing State and Maybe 3 | --- 4 | 5 | Now that you've spent some time building general abstractions for working with 6 | state and failing computations we want to go back through and look for 7 | commonalities. 8 | 9 | First, go back through all the code you wrote for Set 1, Find all the 10 | generalized functions and copy their type signatures (not the code) into a new 11 | file. You can skip the functions that solved special case exercises. Then do 12 | the same thing for Set 2 and copy those type signatures into the same file. 13 | 14 | Now look at these type signatures and look for functions from Set 1 that have 15 | a similar type signature pattern to functions from Set 2. The type signatures 16 | obviously won't be the same, but there are similar patterns in both sets. You 17 | should come up with two pairs of function equivalences. 18 | 19 | Here is the hex encoded answer. But again, spend some effort trying to see 20 | this correspondence for yourself. 21 | 22 | 67656E54776F207E3D206C696E6B2C2067656E6572616C42207E3D20794C696E6B 23 | 24 | Now that you have the correspondences, for each pair write a more general type 25 | signature that works for both the `Gen` version and the `Maybe` version. Think 26 | back to things we've done before. If two signatures are exactly the same 27 | except for one difference, replace that difference with a type variable. It 28 | doesn't matter what letter you use for the type variable as long as it is 29 | different from all the other type variables that occur in the signature. But 30 | just to make your life easier we'll give you a hint: use the letter `m`. 31 | Here's the hex-encoded answer: 32 | 33 | 67656E54776F2C206C696E6B0A20203A3A206D2061202D3E202861202D3E206D206229202D3E206D20620A0A67656E6572616C422C20794C696E6B0A20203A3A202861202D3E2062202D3E206329202D3E206D2061202D3E206D2062202D3E206D2063 34 | 35 | You won't be able to implement these functions yet. We're just writing type 36 | signatures right now and looking at the patterns involved. 37 | 38 | [Previous Page](set4.html) - [Next Page](ex4-2.html) 39 | -------------------------------------------------------------------------------- /monad-challenges-code/hakyll/pages/ex4-2.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: A Missed Generalization 3 | --- 4 | 5 | We just found an equivalence between `generalB` and `yLink`. In Set 2 we implemented 6 | `yLink` in terms of `link` without using any cases. But in Set 1 you might not have 7 | implemented `generalB` in terms of `genTwo`. Go back and look at your `generalB` 8 | implementation and if you didn't write it in terms of `genTwo`, do that now and 9 | call it `generalB2`. Doing this should get rid of the state threading between 10 | generators. 11 | 12 | Re-implement `repRandom` in terms of `generalA`, `genTwo`, and 13 | `mkGen`. Note that by using `generalA`, `genTwo` and `mkGen` you 14 | should not need to have a `seed` variable in your code for `repRandom` anywhere. 15 | 16 | [Previous Page](ex4-1.html) - [Next Page](ex4-3.html) 17 | -------------------------------------------------------------------------------- /monad-challenges-code/hakyll/pages/ex4-3.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Formalizing the Pattern 3 | --- 4 | 5 | Now that we have identified a pattern that we want to abstract, we need to 6 | formalize it somehow. When we did an abstraction like this before, we took a 7 | repeated type signature expression and created a shorthand name for it with a 8 | type synonym and a type variable. The modification only affected type 9 | signatures and had nothing to do with the implementations. Now we are trying 10 | to find commonalities across two implementations of completely different 11 | problems, so a simple type synonym is not going to be enough for what we want to 12 | do. 13 | 14 | Clearly the random number example and the failing computation example have 15 | some similarities, but clearly they also have some differences. We're trying 16 | to find the smallest set of fundamental primitives that have to be different. 17 | We have two candidates for those things: `genTwo`/`link` and `generalB2`/`yLink`. We 18 | just saw that `generalB2`/`yLink` can be written in terms of `genTwo`/`link`, so let's 19 | assume that the `genTwo`/`link` abstraction is part of the fundamental set of 20 | primitives. 21 | 22 | Whatever this pattern is that is common between random number generation and 23 | failing computations, we need to give it a name so we can talk about it more 24 | easily. Let's call it a monad! You know how companies these days have been 25 | giving themselves nonsensical names that allow them to completely define their 26 | brand without competing with their customers' preconceived notions of what 27 | common words mean? We're doing the same thing here. (Well, mathematicians did it 28 | for us a while back.) Now that we have a name we need to create a type class: 29 | 30 | class Monad m where 31 | 32 | The generalized type signature for `genTwo`/`link` that you came up with in 33 | challenge #1 is one of the ones we want to put into our type class, and if 34 | you used the type variable `m`, you should be able to drop it in. All we need 35 | is a name. Let's use the name `bind`. 36 | 37 | Now that we have part of our type class your task is to create a single unified 38 | implementation for `generalB2`/`yLink`. Most of it should be the same, but you'll 39 | find that there is one part that is different for the two. Make that part into 40 | the second function of the type class. Call this function `return`. Figure out 41 | what the type signature should be. We've seen this pattern before in Set 1 and 42 | Set 3. 43 | 44 | [Previous Page](ex4-2.html) - [Next Page](ex4-4.html) 45 | -------------------------------------------------------------------------------- /monad-challenges-code/hakyll/pages/ex4-4.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Creating Instances 3 | --- 4 | 5 | Now you have a `Monad` type class with two functions: `bind` and `return`. If 6 | you've heard anything about monads in the past, this might sound familiar. 7 | But don't go off and look at existing monad code yet. You have a lot more 8 | ground to discover yourself. 9 | 10 | The next thing you need to do is create instances of your `Monad` type class for 11 | the three types we've been working with: `Gen`, `Maybe`, and `[]`. You can do this for 12 | `Maybe` and `[]` with no trouble. But `Gen` won't work because it is a type synonym. 13 | First you need to replace your type synonym with a `newtype`. Don't go back and 14 | modify your old code. We'll be referring back to that in the future. Do this 15 | work in a new file Set4.hs (again using the same header we've been using and 16 | importing `MCPrelude` instead of Haskell's Prelude). You can import Set2 because 17 | that has your `Maybe` data type and associated code which can be reused. But don't 18 | import Set1. When you are finished with this challenge you should have a Set4.hs 19 | file with a `Monad` type class, a `Gen` newtype, and three instances. 20 | 21 | Along with your `Gen` newtype you also might want to write a helper function to 22 | make it easier to get your random values out: 23 | 24 | evalGen :: Gen a -> Seed -> a 25 | 26 | If you did all the exercises properly this should be pretty straightforward. The 27 | `Monad` instance for `Gen` may be a little bit less obvious because this time you'll 28 | have to do some newtype wrangling. 29 | 30 | If you need to brush up your knowledge of some of these topics, check out [this 31 | chapter on 32 | newtypes](http://learnyouahaskell.com/functors-applicative-functors-and-monoids#the-newtype-keyword) 33 | or [this chapter on type 34 | classes](http://learnyouahaskell.com/making-our-own-types-and-typeclasses#typeclasses-102) 35 | in Learn You a Haskell. 36 | 37 | If you're having trouble with the newtype instance here's a hex encoded code 38 | template to get you started: 39 | 40 | 6E6577747970652047656E2061203D2047656E207B2072756E47656E203A3A2053656564202D3E2028612C205365656429207DDADA696E7374616E6365204D6F6E61642047656E207768657265DA2020202072657475726E203D202E2E2EDA2020202062696E64203D202E2E2E 41 | 42 | Again, if possible try not to use this. We're still leaving the hard bits out. 43 | These things are worth fighting with for awhile. 44 | 45 | [Previous Page](ex4-3.html) - [Next Page](ex4-5.html) 46 | -------------------------------------------------------------------------------- /monad-challenges-code/hakyll/pages/ex4-5.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Revisiting Other Generic Functions 3 | --- 4 | 5 | Now that we have our monad type class and an understanding of what motivated it, 6 | go back and rewrite the generic functions from sets 1, 2, and 3 using your `Monad` 7 | type class. But since we're rewriting them generically we need to give them new 8 | names. Here are the functions that you should rewrite and the new names that you 9 | should use. From set 1: 10 | 11 | * `repRandom` (`sequence`) 12 | * `generalB` (`liftM2`) 13 | 14 | From set 2: 15 | 16 | * `chain` (`=<<`) 17 | * `yLink` (`liftM2`) 18 | * `combine` (`join`) 19 | 20 | From set 3: 21 | 22 | * `allCombs` (`liftM2`) 23 | * `allCombs3` (`liftM3`) 24 | * `combStep` (`ap`) 25 | 26 | You only need to write `liftM2` once. But instead of using `Gen`, `Maybe`, or `[]` you 27 | use `m` in its place with the `Monad m` type class constraint. Try to do this set 28 | without looking at the code you used before. Just copy all the type signatures 29 | and redo the work, but this time do everything in terms of `return` and `bind`. 30 | 31 | [Previous Page](ex4-4.html) - [Next Page](ex4-6.html) 32 | -------------------------------------------------------------------------------- /monad-challenges-code/hakyll/pages/ex4-6.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Using the abstraction 3 | --- 4 | 5 | Now your Set4.hs module has fairly decent set of abstract tools all built on 6 | the `Monad` interface. Now go back through sets 1, 2, and 3 and redo them all 7 | using the library of functions you built up in Set4.hs. Since `Gen` is now a 8 | newtype you will have to make some changes to the functions that use it. This 9 | may seem like a waste of time, but it will develop your familiarity with the 10 | names actually used by Haskell's monad library. This is the core of developing 11 | a working knowledge of monads. The first three sets were the motivation. Now 12 | we're getting to the real world use. 13 | 14 | [Previous Page](ex4-5.html) - [Next Page](set5.html) 15 | -------------------------------------------------------------------------------- /monad-challenges-code/hakyll/pages/ex5-1.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Do Notation 3 | --- 4 | 5 | Now that you've spent some time using the monad abstraction, you may have seen 6 | that while it does what we need, it's a little more verbose than we would like. 7 | This is where Haskell's do notation comes to the rescue. When you write the 8 | following do block: 9 | 10 | rule1 = do 11 | foo <- calcFoo 12 | bar foo 13 | 14 | GHC automatically desugars this to 15 | 16 | rule1 = bind calcFoo (\foo -> bar foo) 17 | 18 | It's important to note what this says about the type signatures of everything 19 | involved. 20 | 21 | rule1 :: m b 22 | calcFoo :: m a 23 | bar :: a -> m b 24 | 25 | The key is that whatever `m` is, it must be the same for all three of these types. 26 | Also, `calcFoo` returns an `m a`, but `bar` takes a plain `a`. The bind function 27 | is responsible for "unboxing" the `m a` and passing the unboxed value to `bar`. 28 | 29 | One thing that trips a lot of people up is what to do when they don't have a 30 | function that looks like `bar`. The version of `bar` they want might instead have 31 | this type `bar :: a -> b` (note that here we're specifically saying that `bar`'s 32 | type signature does NOT have an `m`). Figure out what to do in this situation. 33 | 34 | Note this challenge isn't concrete. We're dealing with more abstract types that 35 | don't have type signatures exactly the same as the ones you've been working 36 | with. We could have written this challenge in a concrete way, but we think it's 37 | important to learn how to think in more abstract terms. So for this go back to 38 | the stuff you have done before and find something that you think can be written 39 | with the do block that `rule1` has. Or better yet, make up a new function that 40 | does it using `Maybe` or `Gen`. 41 | 42 | [Previous Page](set5.html) - [Next Page](ex5-2.html) 43 | -------------------------------------------------------------------------------- /monad-challenges-code/hakyll/pages/ex5-2.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Do Notation – operators 3 | --- 4 | 5 | In the last exercise, we noted that the following code: 6 | 7 | rule1 = do 8 | foo <- calcFoo 9 | bar foo 10 | 11 | Is automatically de-sugared to: 12 | 13 | rule1 = bind calcFoo (\foo -> bar foo) 14 | 15 | In Haskell, the `bind` function is conventionally written as the `>>=` 16 | operator, which can be written infix style like this: 17 | 18 | rule1 = calcFoo >>= (\foo -> bar foo) 19 | 20 | In order for the do syntax to work correctly, we need to change our `Monad` class 21 | to have a `>>=` operator. Create a class like this: 22 | 23 | class Monad m where 24 | (>>=) :: m a -> (a -> m b) -> m b 25 | return :: a -> m a 26 | 27 | fail :: String -> m a 28 | fail = undefined 29 | 30 | (Note: for historical reasons, `Monad` is required to have a `fail` function. We 31 | will not be concerning ourselves with failure here, so we just leave this as undefined. 32 | When you implement `Monad` for your own data types, you should only implement the `>>=` and 33 | `return` functions) 34 | 35 | In the next few sections, we will be rewriting the earlier exercises using do syntax. 36 | 37 | [Previous Page](ex5-1.html) - [Next Page](ex5-3.html) 38 | -------------------------------------------------------------------------------- /monad-challenges-code/hakyll/pages/ex5-3.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Do Notation – Set 1 3 | --- 4 | 5 | First, create an instance of `Monad` for your `Gen` newtype, similar to what you did in Set 4. 6 | You will also probably want to create an `evalGen :: Gen a -> Seed -> a` function as well. 7 | 8 | With your `Monad` instance, you should be able to use do syntax. Recall that in Set 1 we had 9 | a function called `rand :: Seed -> (Integer, Seed)`. Create a new function 10 | `makeRandom :: Gen Integer` which wraps the `rand` function inside your new type. 11 | 12 | Next, use do syntax to re-create the following function from Set 1: 13 | 14 | fiveRands :: Gen [Integer] 15 | 16 | To check that you created this function correctly, recall that the product of the five numbers 17 | you generate when passing in a seed of `mkSeed 1` is 8681089573064486461641871805074254223660. 18 | 19 | Once that has been created correctly, create `randLetter :: Gen Char` and use it to create 20 | `randString3 :: Gen String`, which creates a `String` of three random characters. If you have an initial seed of 1, when you use [this site](http://www.xorbin.com/tools/sha256-hash-calculator) to 21 | calculate the SHA-256 hash of the output of randString3, you get 22 | 9d475eb78d3e38085220ed6ebde9d8f7d26540bb1c8f9382479c3acd4c8c94a3. 23 | 24 | Lastly, go ahead and create this general function: 25 | 26 | generalPair :: Gen a -> Gen b -> Gen (a, b) 27 | 28 | [Previous Page](ex5-2.html) - [Next Page](ex5-4.html) 29 | -------------------------------------------------------------------------------- /monad-challenges-code/hakyll/pages/ex5-4.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Do Notation – Set 2 3 | --- 4 | 5 | As with the previous set, you will want to create a `Monad` instance for your `Maybe data` type. 6 | 7 | Once that is accomplished, rewrite the following functions using do syntax: 8 | 9 | queryGreek :: GreekData -> String -> Maybe Double 10 | addSalaries :: [(String, Integer)] -> String -> String -> Maybe Integer 11 | tailProd :: Num a => [a] -> Maybe a 12 | tailSum :: Num a => [a] -> Maybe a 13 | tailMax :: Ord a => [a] -> Maybe a 14 | 15 | You should import Set2 so that you can use the helper functions you wrote in that set. 16 | 17 | You can test `queryGreek` with the following assertions: 18 | 19 | queryGreek greekDataA "alpha" == Just 2.0 20 | queryGreek greekDataA "beta" == Nothing 21 | queryGreek greekDataA "gamma" == Just 3.3333333333333335 22 | queryGreek greekDataA "delta" == Nothing 23 | queryGreek greekDataA "zeta" == Nothing 24 | 25 | queryGreek greekDataB "rho" == Nothing 26 | queryGreek greekDataB "phi" == Just 0.24528301886792453 27 | queryGreek greekDataB "chi" == Just 9.095238095238095 28 | queryGreek greekDataB "psi" == Nothing 29 | queryGreek greekDataB "omega" == Just 24.0 30 | 31 | [Previous Page](ex5-3.html) - [Next Page](ex5-5.html) 32 | -------------------------------------------------------------------------------- /monad-challenges-code/hakyll/pages/ex5-5.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Do Notation – Set 3 3 | --- 4 | 5 | Again, create a `Monad` instance for your `[]` type. Import your `Card` data constructor from Set 3. Using do syntax, implement the following functions: 6 | 7 | allPairs :: [a] -> [b] -> [(a,b)] 8 | allCards :: [Int] -> [String] -> [Card] 9 | allCombs3 :: (a -> b -> c -> d) -> [a] -> [b] -> [c] -> [d] 10 | 11 | [Previous Page](ex5-4.html) 12 | -------------------------------------------------------------------------------- /monad-challenges-code/hakyll/pages/set1.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Set 1: Random Numbers" 3 | --- 4 | 5 | It may not seem like the problems in this set have much to do with monads at 6 | first, but bear with us. We are taking you through the motivations for monads 7 | in a step by step structured way. It may be tempting to skip over this stuff. 8 | DON'T DO IT. Later problem sets build on things you did here. Stick with us 9 | and try to absorb the ideas and patterns. 10 | 11 | 1. [Random number generation](ex1-1.html) 12 | 2. [Random character generation](ex1-2.html) 13 | 3. [More generators](ex1-3.html) 14 | 4. [Generalizing random pairs](ex1-4.html) 15 | 5. [Generalizing lists of generators](ex1-5.html) 16 | 6. [Threading the random number state](ex1-6.html) 17 | 18 | [Next Page](ex1-1.html) 19 | -------------------------------------------------------------------------------- /monad-challenges-code/hakyll/pages/set2.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Set 2: Failing Computations" 3 | --- 4 | 5 | This set deals with programming patterns that arise when dealing with 6 | computations that might fail. Failing computations come up all the time in 7 | the real world and Haskell can provide great tools for dealing with them. 8 | 9 | 1. [The Maybe type](ex2-1.html) 10 | 2. [Build a library of things that can fail](ex2-2.html) 11 | 3. [Chains of failing computations](ex2-3.html) 12 | 4. [Generalizing chains of failures](ex2-4.html) 13 | 5. [Chaining variations](ex2-5.html) 14 | 6. [Tailprod](ex2-6.html) 15 | 16 | [Previous Page](ex1-6.html) - [Next Page](ex2-1.html) 17 | -------------------------------------------------------------------------------- /monad-challenges-code/hakyll/pages/set3.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Set 3: Combinations" 3 | --- 4 | 5 | In this set we are going to investigate lists. As usual, make sure you start 6 | with the standard block of header code we've been using for these challenges. 7 | If you don't do this, you might inadvertently solve these problems in a way 8 | that misses the whole point of the exercise. 9 | 10 | 1. [Generating combinations](ex3-1.html) 11 | 2. [Poker hands](ex3-2.html) 12 | 3. [Generalizing pairs and cards](ex3-3.html) 13 | 4. [Combinations of three things](ex3-4.html) 14 | 5. [Combinations of more things](ex3-5.html) 15 | 16 | [Previous Page](ex2-6.html) - [Next Page](ex3-1.html) 17 | -------------------------------------------------------------------------------- /monad-challenges-code/hakyll/pages/set4.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Set 4: Common Abstraction" 3 | --- 4 | 5 | 1. [Generalizing State and Maybe](ex4-1.html) 6 | 2. [A missed generalization](ex4-2.html) 7 | 3. [Formalizing the pattern](ex4-3.html) 8 | 4. [Creating instances](ex4-4.html) 9 | 5. [Revisiting other generic functions](ex4-5.html) 10 | 6. [Using the abstraction](ex4-6.html) 11 | 12 | [Previous Page](ex3-5.html) - [Next Page](ex4-1.html) -------------------------------------------------------------------------------- /monad-challenges-code/hakyll/pages/set5.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Set 5: Do Notation" 3 | --- 4 | 5 | In this set, we are going to rewrite the challenges we completed in earlier sets using 6 | do syntax. Do syntax is a common form of syntactic sugar that you will see when writing Haskell, 7 | but it is functionally equivalent to previous functions we've already created. 8 | 9 | 1. [Do notation](ex5-1.html) 10 | 2. [Do notation – operators](ex5-2.html) 11 | 3. [Do notation – set 1](ex5-3.html) 12 | 4. [Do notation – set 2](ex5-4.html) 13 | 5. [Do notation – set 3](ex5-5.html) 14 | 15 | [Previous Page](ex4-6.html) - [Next Page](ex5-1.html) 16 | -------------------------------------------------------------------------------- /monad-challenges-code/hakyll/regen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | SITE_DIR=../result/bin 4 | $SITE_DIR/site clean 5 | $SITE_DIR/site build 6 | rm ../../docs/index.html 7 | rm -fr ../../docs/pages 8 | cp -R _site/* ../../docs 9 | -------------------------------------------------------------------------------- /monad-challenges-code/hakyll/site.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | 3 | import Control.Monad.Trans 4 | import Data.Monoid 5 | import Hakyll 6 | import System.FilePath 7 | 8 | 9 | -------------------------------------------------------------------------------- 10 | main :: IO () 11 | main = hakyll $ do 12 | match "index.md" $ do 13 | route $ setExtension "html" 14 | compile $ do 15 | ctx <- getContextWithSets 16 | pandocCompiler 17 | >>= loadAndApplyTemplate "templates/default.html" ctx 18 | >>= relativizeUrls 19 | 20 | match "pages/*.md" $ do 21 | route $ setExtension "html" 22 | compile $ do 23 | content <- pandocCompiler 24 | saveSnapshot "content" content 25 | ctx <- getContextWithSets 26 | loadAndApplyTemplate "templates/default.html" ctx content 27 | >>= relativizeUrls 28 | 29 | match "templates/*" $ compile templateCompiler 30 | 31 | getContextWithSets = do 32 | sets <- loadAllSnapshots "pages/set*" "content" 33 | return $ 34 | listField "sets" defaultContext (return sets) <> 35 | defaultContext 36 | 37 | routePages :: Identifier -> FilePath 38 | routePages i = "pages" file <.> "html" 39 | where 40 | file = takeBaseName $ toFilePath i 41 | 42 | -------------------------------------------------------------------------------- /monad-challenges-code/hakyll/templates/default.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | The Monad Challenges 6 | 7 | 8 | 9 | 10 | 11 | 14 | 15 | 16 |
17 |
18 |

The Monad Challenges

19 |

A set of challenges for jump starting your understanding of monads.

20 | 21 |

Outline

22 | 23 | $for(sets)$ 24 |

$title$

25 | $endfor$ 26 | 27 | 32 | 33 |

This project is maintained by mightybyte

34 | 35 |

36 | Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. 37 |

38 | 39 |
40 |
41 |

$title$

42 | 43 | $body$ 44 | 45 |
46 |
47 | 48 | 52 | 58 | 59 | -------------------------------------------------------------------------------- /monad-challenges-code/monad-challenges-code.cabal: -------------------------------------------------------------------------------- 1 | name: monad-challenges-code 2 | version: 0.1.0.0 3 | license: BSD3 4 | license-file: LICENSE 5 | author: Doug Beardsley 6 | maintainer: mightybyte@gmail.com 7 | synopsis: The best way to learn monads 8 | description: Series of challenges to help build intuition about monads 9 | category: Education 10 | build-type: Simple 11 | cabal-version: >=1.10 12 | 13 | Tested-With: 14 | GHC == 7.6.3, 15 | GHC == 7.8.4, 16 | GHC == 7.10.3, 17 | GHC == 8.0.2, 18 | GHC == 8.2.2, 19 | GHC == 8.4.4, 20 | GHC == 8.6.3, 21 | GHC == 8.8.3, 22 | GHC == 8.10.1 23 | 24 | library 25 | exposed-modules: MCPrelude 26 | other-extensions: Trustworthy, CPP, NoImplicitPrelude, BangPatterns 27 | build-depends: base >= 4 && < 5 28 | hs-source-dirs: src 29 | default-language: Haskell2010 30 | 31 | executable site 32 | hs-source-dirs: hakyll 33 | main-is: site.hs 34 | build-depends: 35 | base 36 | , filepath 37 | , hakyll 38 | , mtl 39 | , transformers 40 | default-language: Haskell2010 41 | --------------------------------------------------------------------------------