diff --git a/docs/a-fistful-of-monads.html b/docs/a-fistful-of-monads.html index 876379c..0b82559 100644 --- a/docs/a-fistful-of-monads.html +++ b/docs/a-fistful-of-monads.html @@ -181,12 +181,12 @@
Make sure you understand applicatives at this point. It’s good if you have a feel for how the various Applicative instances work and what kind of computations they represent, because monads are nothing more than taking our existing applicative knowledge and upgrading it. -
A value of type Maybe a represents a value of type @@ -422,12 +422,12 @@
Just a reminder: return is nothing like the return that’s in most other languages. It doesn’t end function execution or anything, it just takes a normal value and puts it in a context. -
When you have non-deterministic values interacting, you can view their computation as a tree where every possible result in a list represents a separate branch. -
Here’s the previous expression rewritten in do notation: diff --git a/docs/assets/css/style.css b/docs/assets/css/style.css index 466bfc8..fe2e5e6 100644 --- a/docs/assets/css/style.css +++ b/docs/assets/css/style.css @@ -80,6 +80,9 @@ pre.code { margin-bottom:25px; background-image:url(https://s3.amazonaws.com/lyah/note.jpg); } +.hintbox > p { + margin-bottom:0px; +} .label { white-space:nowrap; font-family:Consolas, "Courier New", monospace; diff --git a/docs/for-a-few-monads-more.html b/docs/for-a-few-monads-more.html index bc3589d..d9f18a9 100644 --- a/docs/for-a-few-monads-more.html +++ b/docs/for-a-few-monads-more.html @@ -219,9 +219,9 @@
Be sure you know what monoids are at this point! Cheers. -
Right now, applyLog takes values of type @@ -1203,7 +1203,7 @@
Assignment in most other languages could be thought of as a stateful computation. For instance, when we do x = 5 in an imperative language, it will usually assign the value @@ -1213,7 +1213,7 @@
This stateful computation, a function that takes a state diff --git a/docs/functionally-solving-problems.html b/docs/functionally-solving-problems.html index 69339c4..8261603 100644 --- a/docs/functionally-solving-problems.html +++ b/docs/functionally-solving-problems.html @@ -40,7 +40,7 @@
Let’s go over the expression 10 4 3 + 2 * - together! First we push 10 on to the stack and the stack is now 10. The next item is 4, so we push it to the stack as well. The stack is now 10, 4. We do the same with 3 and the stack is now 10, 4, 3. And now, we encounter an operator, namely +! We pop the two top numbers from the stack (so now the stack is just 10), add those numbers together and push that result to the stack. The stack is now 10, 7. We push 2 to the stack, the stack for now is 10, 7, 2. We’ve encountered an operator again, so let’s pop 7 and 2 off the stack, multiply them and push that result to the stack. Multiplying 7 and 2 produces a 14, so the stack we have now is 10, 14. Finally, there’s a -. We pop 10 and 14 from the stack, subtract 14 from 10 and push that back. The number on the stack is now -4 and because there are no more numbers or operators in our expression, that’s our result!
Now that we know how we’d calculate any RPN expression by hand, let’s think about how we could make a Haskell function that takes as its parameter a string that contains a RPN expression, like "10 4 3 + 2 * -" and gives us back its result.
What would the type of that function be? We want it to take a string as a parameter and produce a number as its result. So it will probably be something like solveRPN :: (Num a) => String -> a.
-Protip: it really helps to first think what the type declaration of a function should be before concerning ourselves with the implementation and then write it down. In Haskell, a function’s type declaration tells us a whole lot about the function, due to the very strong type system.
Cool. When implementing a solution to a problem in Haskell, it’s also good to think back on how you did it by hand and maybe try to see if you can gain any insight from that. Here we see that we treated every number or operator that was separated by a space as a single item. So it might help us if we start by breaking a string like "10 4 3 + 2 * -" into a list of items like ["10","4","3","+","2","*","-"].
Next up, what did we do with that list of items in our head? We went over it from left to right and kept a stack as we did that. Does the previous sentence remind you of anything? Remember, in the section about folds, we said that pretty much any function where you traverse a list from left to right or right to left one element by element and build up (accumulate) some result (whether it’s a number, a list, a stack, whatever) can be implemented with a fold.
@@ -160,7 +160,7 @@Alright, can you figure out what the shortest path to the first crossroads (the first blue dot on A, marked A1) on road A is? That’s pretty trivial. We just see if it’s shorter to go directly forward on A or if it’s shorter to go forward on B and then cross over. Obviously, it’s cheaper to go forward via B and then cross over because that takes 40 minutes, whereas going directly via A takes 50 minutes. What about crossroads B1? Same thing. We see that it’s a lot cheaper to just go directly via B (incurring a cost of 10 minutes), because going via A and then crossing over would take us a whole 80 minutes!
Now we know what the cheapest path to A1 is (go via B and then cross over, so we’ll say that’s B, C with a cost of 40) and we know what the cheapest path to B1 is (go directly via B, so that’s just B, going at 10). Does this knowledge help us at all if we want to know the cheapest path to the next crossroads on both main roads? Gee golly, it sure does!
Let’s see what the shortest path to A2 would be. To get to A2, we’ll either go directly to A2 from A1 or we’ll go forward from B1 and then cross over (remember, we can only move forward or cross to the other side). And because we know the cost to A1 and B1, we can easily figure out what the best path to A2 is. It costs 40 to get to A1 and then 5 to get from A1 to A2, so that’s B, C, A for a cost of 45. It costs only 10 to get to B1, but then it would take an additional 110 minutes to go to B2 and then cross over! So obviously, the cheapest path to A2 is B, C, A. In the same way, the cheapest way to B2 is to go forward from A1 and then cross over.
-Maybe you’re asking yourself: but what about getting to A2 by first crossing over at B1 and then going on forward? Well, we already covered crossing from B1 to A1 when we were looking for the best way to A1, so we don’t have to take that into account in the next step as well.
Now that we have the best path to A2 and B2, we can repeat this indefinitely until we reach the end. Once we’ve gotten the best paths for A4 and B4, the one that’s cheaper is the optimal path!
So in essence, for the second section, we just repeat the step we did at first, only we take into account what the previous best paths on A and B. We could say that we also took into account the best paths on A and on B in the first step, only they were both empty paths with a cost of 0.
Here’s a summary. To get the best path from Heathrow to London, we do this: first we see what the best path to the next crossroads on main road A is. The two options are going directly forward or starting at the opposite road, going forward and then crossing over. We remember the cost and the path. We use the same method to see what the best path to the next crossroads on main road B is and remember that. Then, we see if the path to the next crossroads on A is cheaper if we go from the previous A crossroads or if we go from the previous B crossroads and then cross over. We remember the cheaper path and then we do the same for the crossroads opposite of it. We do this for every section until we reach the end. Once we’ve reached the end, the cheapest of the two paths that we have is our optimal path!
@@ -183,7 +183,7 @@This is pretty much perfect! It’s as simple as it goes and I have a feeling it’ll work perfectly for implementing our solution. Section is a simple algebraic data type that holds three integers for the lengths of its three road parts. We introduce a type synonym as well, saying that RoadSystem is a list of sections.
-We could also use a triple of (Int, Int, Int) to represent a road section. Using tuples instead of making your own algebraic data types is good for some small localized stuff, but it’s usually better to make a new type for things like this. It gives the type system more information about what’s what. We can use (Int, Int, Int) to represent a road section or a vector in 3D space and we can operate on those two, but that allows us to mix them up. If we use Section and Vector data types, then we can’t accidentally add a vector to a section of a road system.
Our road system from Heathrow to London can now be represented like this:
heathrowToLondon :: RoadSystem @@ -200,7 +200,7 @@Heathrow to London
We’re going to have to walk over the list with the sections from left to right and keep the optimal path on A and optimal path on B as we go along. We’ll accumulate the best path as we walk over the list, left to right. What does that sound like? Ding, ding, ding! That’s right, A LEFT FOLD!
When doing the solution by hand, there was a step that we repeated over and over again. It involved checking the optimal paths on A and B so far and the current section to produce the new optimal paths on A and B. For instance, at the beginning the optimal paths were [] and [] for A and B respectively. We examined the section Section 50 10 30 and concluded that the new optimal path to A1 is [(B,10),(C,30)] and the optimal path to B1 is [(B,10)]. If you look at this step as a function, it takes a pair of paths and a section and produces a new pair of paths. The type is (Path, Path) -> Section -> (Path, Path). Let’s go ahead and implement this function, because it’s bound to be useful.
-Hint: it will be useful because (Path, Path) -> Section -> (Path, Path) can be used as the binary function for a left fold, which has to have a type of a -> b -> a
roadStep :: (Path, Path) -> Section -> (Path, Path) roadStep (pathA, pathB) (Section a b c) = @@ -228,7 +228,7 @@Heathrow to London
([(C,30),(B,10)],[(B,10)])
Remember, the paths are reversed, so read them from right to left. From this we can read that the best path to the next A is to start on B and then cross over to A and that the best path to the next B is to just go directly forward from the starting point at B.
-Optimization tip: when we do priceA = sum $ map snd pathA, we’re calculating the price from the path on every step. We wouldn’t have to do that if we implemented roadStep as a (Path, Path, Int, Int) -> Section -> (Path, Path, Int, Int) function where the integers represent the best price on A and B.
Now that we have a function that takes a pair of paths and a section and produces a new optimal path, we can just easily do a left fold over a list of sections. roadStep is called with ([],[]) and the first section and returns a pair of optimal paths to that section. Then, it’s called with that pair of paths and the next section and so on. When we’ve walked over all the sections, we’re left with a pair of optimal paths and the shorter of them is our answer. With this in mind, we can implement optimalPath.
optimalPath :: RoadSystem -> Path diff --git a/docs/functors-applicative-functors-and-monoids.html b/docs/functors-applicative-functors-and-monoids.html index 8d9f158..d360188 100644 --- a/docs/functors-applicative-functors-and-monoids.html +++ b/docs/functors-applicative-functors-and-monoids.html @@ -38,7 +38,7 @@Functors redux
![]()
We’ve already talked about functors in their own little section. If you haven’t read it yet, you should probably give it a glance right now, or maybe later when you have more time. Or you can just pretend you read it.
Still, here’s a quick refresher: Functors are things that can be mapped over, like lists, Maybes, trees, and such. In Haskell, they’re described by the typeclass Functor, which has only one typeclass method, namely fmap, which has a type of fmap :: (a -> b) -> f a -> f b. It says: give me a function that takes an a and returns a b and a box with an a (or several of them) inside it and I’ll give you a box with a b (or several of them) inside it. It kind of applies the function to the element inside the box.
-A word of advice. Many times the box analogy is used to help you get some intuition for how functors work, and later, we’ll probably use the same analogy for applicative functors and monads. It’s an okay analogy that helps people understand functors at first, just don’t take it too literally, because for some functors the box analogy has to be stretched really thin to still hold some truth. A more correct term for what a functor is would be computational context. The context might be that the computation can have a value or it might have failed (Maybe and Either a) or that there might be more values (lists), stuff like that.+A word of advice. Many times the box analogy is used to help you get some intuition for how functors work, and later, we’ll probably use the same analogy for applicative functors and monads. It’s an okay analogy that helps people understand functors at first, just don’t take it too literally, because for some functors the box analogy has to be stretched really thin to still hold some truth. A more correct term for what a functor is would be computational context. The context might be that the computation can have a value or it might have failed (Maybe and Either a) or that there might be more values (lists), stuff like that.
If we want to make a type constructor an instance of Functor, it has to have a kind of * -> *, which means that it has to take exactly one concrete type as a type parameter. For example, Maybe can be made an instance because it takes one type parameter to produce a concrete type, like Maybe Int or Maybe String. If a type constructor takes two parameters, like Either, we have to partially apply the type constructor until it only takes one type parameter. So we can’t write instance Functor Either where, but we can write instance Functor (Either a) where and then if we imagine that fmap is only for Either a, it would have a type declaration of fmap :: (b -> c) -> Either a b -> Either a c. As you can see, the Either a part is fixed, because Either a takes only one type parameter, whereas just Either takes two so fmap :: (b -> c) -> Either b -> Either c wouldn’t really make sense.
We’ve learned by now how a lot of types (well, type constructors really) are instances of Functor, like [], Maybe, Either a and a Tree type that we made on our own. We saw how we can map functions over them for great good. In this section, we’ll take a look at two more instances of functor, namely IO and (->) r.
If some value has a type of, say, IO String, that means that it’s an I/O action that, when performed, will go out into the real world and get some string for us, which it will yield as a result. We can use <- in do syntax to bind that result to a name. We mentioned that I/O actions are like boxes with little feet that go out and fetch some value from the outside world for us. We can inspect what they fetched, but after inspecting, we have to wrap the value back in IO. By thinking about this box with little feet analogy, we can see how IO acts like a functor.
@@ -84,7 +84,7 @@Functors redux
As you probably know, intersperse '-' . reverse . map toUpper is a function that takes a string, maps toUpper over it, the applies reverse to that result and then applies intersperse '-' to that result. It’s like writing (\xs -> intersperse '-' (reverse (map toUpper xs))), only prettier.
Another instance of Functor that we’ve been dealing with all along but didn’t know was a Functor is (->) r. You’re probably slightly confused now, since what the heck does (->) r mean? The function type r -> a can be rewritten as (->) r a, much like we can write 2 + 3 as (+) 2 3. When we look at it as (->) r a, we can see (->) in a slightly different light, because we see that it’s just a type constructor that takes two type parameters, just like Either. But remember, we said that a type constructor has to take exactly one type parameter so that it can be made an instance of Functor. That’s why we can’t make (->) an instance of Functor, but if we partially apply it to (->) r, it doesn’t pose any problems. If the syntax allowed for type constructors to be partially applied with sections (like we can partially apply + by doing (2+), which is the same as (+) 2), you could write (->) r as (r ->). How are functions functors? Well, let’s take a look at the implementation, which lies in Control.Monad.Instances
-We usually mark functions that take anything and return anything as a -> b. r -> a is the same thing, we just used different letters for the type variables.
instance Functor ((->) r) where fmap f g = (\x -> f (g x)) @@ -127,7 +127,7 @@Functors redux
fmap (replicate 3) :: (Functor f) => f a -> f [a]
The expression fmap (*2) is a function that takes a functor f over numbers and returns a functor over numbers. That functor can be a list, a Maybe , an Either String, whatever. The expression fmap (replicate 3) will take a functor over any type and return a functor over a list of elements of that type.
-When we say a functor over numbers, you can think of that as a functor that has numbers in it. The former is a bit fancier and more technically correct, but the latter is usually easier to get.
This is even more apparent if we partially apply, say, fmap (++"!") and then bind it to a name in GHCI.
You can think of fmap as either a function that takes a function and a functor and then maps that function over the functor, or you can think of it as a function that takes a function and lifts that function so that it operates on functors. Both views are correct and in Haskell, equivalent.
The type fmap (replicate 3) :: (Functor f) => f a -> f [a] means that the function will work on any functor. What exactly it will do depends on which functor we use it on. If we use fmap (replicate 3) on a list, the list’s implementation for fmap will be chosen, which is just map. If we use it on a Maybe a, it’ll apply replicate 3 to the value inside the Just, or if it’s Nothing, then it stays Nothing.
@@ -294,7 +294,7 @@Yo! Quick reminder: type variables are independent of parameter names or other value names. The f in the function declaration here is a type variable with a class constraint saying that any type constructor that replaces f should be in the Functor typeclass. The f in the function body denotes a function that we map over x. The fact that we used f to represent both of those doesn’t mean that they somehow represent the same thing.
By using <$>, the applicative style really shines, because now if we want to apply a function f between three applicative functors, we can write f <$> x <*> y <*> z. If the parameters weren’t applicative functors but normal values, we’d write f x y z.
Let’s take a closer look at how this works. We have a value of Just "johntra" and a value of Just "volta" and we want to join them into one String inside a Maybe functor. We do this:
@@ -392,7 +392,7 @@Applicative functors
If you ever find yourself binding some I/O actions to names and then calling some function on them and presenting that as the result by using return, consider using the applicative style because it’s arguably a bit more concise and terse.
Another instance of Applicative is (->) r, so functions. They are rarely used with the applicative style outside of code golf, but they’re still interesting as applicatives, so let’s take a look at how the function instance is implemented.
-If you’re confused about what (->) r means, check out the previous section where we explain how (->) r is a functor.
instance Applicative ((->) r) where pure x = (\_ -> x) @@ -446,7 +446,7 @@-Applicative functors
ghci> getZipList $ (,,) <$> ZipList "dog" <*> ZipList "cat" <*> ZipList "rat" [('d','c','r'),('o','a','a'),('g','t','t')]
The (,,) function is the same as \x y z -> (x,y,z). Also, the (,) function is the same as \x y -> (x,y).
Aside from zipWith, the standard library has functions such as zipWith3, zipWith4, all the way up to 7. zipWith takes a function that takes two parameters and zips two lists with it. zipWith3 takes a function that takes three parameters and zips three lists with it, and so on. By using zip lists with an applicative style, we don’t have to have a separate zip function for each number of lists that we want to zip together. We just use the applicative style to zip together an arbitrary amount of lists with a function, and that’s pretty cool.
Control.Applicative defines a function that’s called liftA2, which has a type of liftA2 :: (Applicative f) => (a -> b -> c) -> f a -> f b -> f c . It’s defined like this:
diff --git a/docs/higher-order-functions.html b/docs/higher-order-functions.html index fa6e753..e459e94 100644 --- a/docs/higher-order-functions.html +++ b/docs/higher-order-functions.html @@ -70,7 +70,7 @@Curried functions
compareWithHundred = compare 100
The type declaration stays the same, because compare 100 returns a function. Compare has a type of (Ord a) => a -> (a -> Ordering) and calling it with 100 returns a (Num a, Ord a) => a -> Ordering. The additional class constraint sneaks up there because 100 is also part of the Num typeclass.
-Infix functions can also be partially applied by using sections. To section an infix function, simply surround it with parentheses and only supply a parameter on one side. That creates a function that takes one parameter and then applies it to the side that’s missing an operand. An insultingly trivial function:
+Yo! Make sure you really understand how curried functions and partial application work because they’re really important!
Infix functions can also be partially applied by using sections. To section an infix function, simply surround it with parentheses and only supply a parameter on one side. That creates a function that takes one parameter and then applies it to the side that’s missing an operand. An insultingly trivial function:
divideByTen :: (Floating a) => a -> a divideByTen = (/10) @@ -100,7 +100,7 @@Some higher-orderism is in order
First of all, notice the type declaration. Before, we didn’t need parentheses because -> is naturally right-associative. However, here, they’re mandatory. They indicate that the first parameter is a function that takes something and returns that same thing. The second parameter is something of that type also and the return value is also of the same type. We could read this type declaration in the curried way, but to save ourselves a headache, we’ll just say that this function takes two parameters and returns one thing. The first parameter is a function (of type a -> a) and the second is that same a. The function can also be Int -> Int or String -> String or whatever. But then, the second parameter to also has to be of that type.
-Note: From now on, we’ll say that functions take several parameters despite each function actually taking only one parameter and returning partially applied functions until we reach a function that returns a solid value. So for simplicity’s sake, we’ll say that a -> a -> a takes two parameters, even though we know what’s really going on under the hood.
The body of the function is pretty simple. We just use the parameter f as a function, applying x to it by separating them with a space and then applying the result to f again. Anyway, playing around with the function:
ghci> applyTwice (+3) 10 @@ -256,7 +256,7 @@Maps and filters
where isLong xs = length xs > 15
We map the chain function to [1..100] to get a list of chains, which are themselves represented as lists. Then, we filter them by a predicate that just checks whether a list’s length is longer than 15. Once we’ve done the filtering, we see how many chains are left in the resulting list.
-Note: This function has a type of numLongChains :: Int because length returns an Int instead of a Num a for historical reasons. If we wanted to return a more general Num a, we could have used fromIntegral on the resulting length.
Using map, we can also do stuff like map (*) [0..], if not for any other reason than to illustrate how currying works and how (partially applied) functions are real values that you can pass around to other functions or put into lists (you just can’t turn them to strings). So far, we’ve only mapped functions that take one parameter over lists, like map (*2) [0..] to get a list of type (Num a) => [a], but we can also do map (*) [0..] without a problem. What happens here is that the number in the list is applied to the function *, which has a type of (Num a) => a -> a -> a. Applying only one parameter to a function that takes two parameters returns a function that takes one parameter. If we map * over the list [0..], we get back a list of functions that only take one parameter, so (Num a) => [a -> a]. map (*) [0..] produces a list like the one we’d get by writing [(0*),(1*),(2*),(3*),(4*),(5*)...
ghci> let listOfFuns = map (*) [0..] diff --git a/docs/input-and-output.html b/docs/input-and-output.html index 37eb50d..194c513 100644 --- a/docs/input-and-output.html +++ b/docs/input-and-output.html @@ -39,7 +39,7 @@Input and Output
Hello, world!
![]()
Up until now, we’ve always loaded our functions into GHCI to test them out and play with them. We’ve also explored the standard library functions that way. But now, after eight or so chapters, we’re finally going to write our first real Haskell program! Yay! And sure enough, we’re going to do the good old "hello, world" schtick.
-Hey! For the purposes of this chapter, I’m going to assume you’re using a unix-y environment for learning Haskell. If you’re on Windows, I’d suggest you download Cygwin, which is a Linux-like environment for Windows, A.K.A. just what you need.+Hey! For the purposes of this chapter, I’m going to assume you’re using a unix-y environment for learning Haskell. If you’re on Windows, I’d suggest you download Cygwin, which is a Linux-like environment for Windows, A.K.A. just what you need.
So, for starters, punch in the following in your favorite text editor:
main = putStrLn "hello, world" @@ -65,7 +65,7 @@Hello, world!
putStrLn "hello, world" :: IO ()We can read the type of putStrLn like this: putStrLn takes a string and returns an I/O action that has a result type of () (i.e. the empty tuple, also know as unit). An I/O action is something that, when performed, will carry out an action with a side-effect (that’s usually either reading from the input or printing stuff to the screen) and will also contain some kind of return value inside it. Printing a string to the terminal doesn’t really have any kind of meaningful return value, so a dummy value of () is used.
-The empty tuple is a value of () and it also has a type of ().+The empty tuple is a value of () and it also has a type of ().
So, when will an I/O action be performed? Well, this is where main comes in. An I/O action will be performed when we give it a name of main and then run our program.
Having your whole program be just one I/O action seems kind of limiting. That’s why we can use do syntax to glue together several I/O actions into one. Take a look at the following example:
@@ -146,7 +146,7 @@Hello, world!
reverseWords = unwords . map reverse . wordsTo get a feel of what it does, you can run it before we go over the code.
-Protip: To run a program you can either compile it and then run the produced executable file by doing ghc --make helloworld and then ./helloworld or you can use the runhaskell command like so: runhaskell helloworld.hs and your program will be executed on the fly.+Protip: To run a program you can either compile it and then run the produced executable file by doing ghc --make helloworld and then ./helloworld or you can use the runhaskell command like so: runhaskell helloworld.hs and your program will be executed on the fly.
First, let’s take a look at the reverseWords function. It’s just a normal function that takes a string like "hey there man" and then calls words with it to produce a list of words like ["hey","there","man"]. Then we map reverse on the list, getting ["yeh","ereht","nam"] and then we put that back into one string by using unwords and the final result is "yeh ereht nam". See how we used function composition here. Without function composition, we’d have to write something like reverseWords st = unwords (map reverse (words st)).
What about main? First, we get a line from the terminal by performing getLine call that line line. And now, we have a conditional expression. Remember that in Haskell, every if must have a corresponding else because every expression has to have some sort of value. We make the if so that when a condition is true (in our case, the line that we entered is blank), we perform one I/O action and when it isn’t, the I/O action under the else is performed. That’s why in an I/O do block, ifs have to have a form of if condition then I/O action else I/O action.
Let’s first take a look at what happens under the else clause. Because, we have to have exactly one I/O action after the else, we use a do block to glue together two I/O actions into one. You could also write that part out as:
@@ -182,7 +182,7 @@Hello, world!
putStrLn $ a ++ " " ++ b
When dealing with I/O do blocks, we mostly use return either because we need to create an I/O action that doesn’t do anything or because we don’t want the I/O action that’s made up from a do block to have the result value of its last action, but we want it to have a different result value, so we use return to make an I/O action that always has our desired result contained and we put it at the end.
-A do block can also have just one I/O action. In that case, it’s the same as just writing the I/O action. Some people would prefer writing then do return () in this case because the else also has a do.
Before we move on to files, let’s take a look at some functions that are useful when dealing with I/O.
putStr is much like putStrLn in that it takes a string as a parameter and returns an I/O action that will print that string to the terminal, only putStr doesn’t jump into a new line after printing out the string while putStrLn does.
@@ -1053,7 +1053,7 @@Randomness
We make a function askForNumber, which takes a random number generator and returns an I/O action that will prompt the user for a number and tell him if he guessed it right. In that function, we first generate a random number and a new generator based on the generator that we got as a parameter and call them randNumber and newGen. Let’s say that the number generated was 7. Then we tell the user to guess which number we’re thinking of. We perform getLine and bind its result to numberString. When the user enters 7, numberString becomes "7". Next, we use when to check if the string the user entered is an empty string. If it is, an empty I/O action of return () is performed, which effectively ends the program. If it isn’t, the action consisting of that do block right there gets performed. We use read on numberString to convert it to a number, so number is now 7.
-Excuse me! If the user gives us some input here that read can’t read (like "haha"), our program will crash with an ugly error message. If you don’t want your program to crash on erroneous input, use reads, which returns an empty list when it fails to read a string. When it succeeds, it returns a singleton list with a tuple that has our desired value as one component and a string with what it didn’t consume as the other.
We check if the number that we entered is equal to the one generated randomly and give the user the appropriate message. And then we call askForNumber recursively, only this time with the new generator that we got, which gives us an I/O action that’s just like the one we performed, only it depends on a different generator and we perform it.
main consists of just getting a random generator from the system and calling askForNumber with it to get the initial action.
Here’s our program in action!
diff --git a/docs/making-our-own-types-and-typeclasses.html b/docs/making-our-own-types-and-typeclasses.html index 0b880e6..01efaae 100644 --- a/docs/making-our-own-types-and-typeclasses.html +++ b/docs/making-our-own-types-and-typeclasses.html @@ -517,7 +517,7 @@Now, a function that gets the value by a key in an association list can have a type of (Eq k) => k -> AssocList k v -> Maybe v. AssocList is a type constructor that takes two types and produces a concrete type, like AssocList Int String, for instance.
-Fonzie says: Aaay! When I talk about concrete types I mean like fully applied types like Map Int String or if we’re dealin’ with one of them polymorphic functions, [a] or (Ord a) => Maybe a and stuff. And like, sometimes me and the boys say that Maybe is a type, but we don’t mean that, cause every idiot knows Maybe is a type constructor. When I apply an extra type to Maybe, like Maybe String, then I have a concrete type. You know, values can only have types that are concrete types! So in conclusion, live fast, love hard and don’t let anybody else use your comb!
Just like we can partially apply functions to get new functions, we can partially apply type parameters and get new type constructors from them. Just like we call a function with too few parameters to get back a new function, we can specify a type constructor with too few type parameters and get back a partially applied type constructor. If we wanted a type that represents a map (from Data.Map) from integers to something, we could either do this:
type IntMap v = Map Int v @@ -527,7 +527,7 @@Type synonyms
type IntMap = Map Int
Either way, the IntMap type constructor takes one parameter and that is the type of what the integers will point to.
-Oh yeah. If you’re going to try and implement this, you’ll probably going to do a qualified import of Data.Map. When you do a qualified import, type constructors also have to be preceded with a module name. So you’d write type IntMap = Map.Map Int.
Make sure that you really understand the distinction between type constructors and value constructors. Just because we made a type synonym called IntMap or AssocList doesn’t mean that we can do stuff like AssocList [(1,2),(4,5),(7,9)]. All it means is that we can refer to its type by using different names. We can do [(1,2),(3,5),(8,9)] :: AssocList Int Int, which will make the numbers inside assume a type of Int, but we can still use that list as we would any normal list that has pairs of integers inside. Type synonyms (and types generally) can only be used in the type portion of Haskell. We’re in Haskell’s type portion whenever we’re defining new types (so in data and type declarations) or when we’re located after a ::. The :: is in type declarations or in type annotations.
Another cool data type that takes two types as its parameters is the Either a b type. This is roughly how it’s defined:
@@ -719,9 +719,9 @@Typeclasses 102
x /= y = not (x == y)
Woah, woah, woah! Some new strange syntax and keywords there! Don’t worry, this will all be clear in a second. First off, when we write class Eq a where, this means that we’re defining a new typeclass and that’s called Eq. The a is the type variable and it means that a will play the role of the type that we will soon be making an instance of Eq. It doesn’t have to be called a, it doesn’t even have to be one letter, it just has to be a lowercase word. Then, we define several functions. It’s not mandatory to implement the function bodies themselves, we just have to specify the type declarations for the functions.
-Some people might understand this better if we wrote class Eq equatable where and then specified the type declarations like (==) :: equatable -> equatable -> Bool.
Anyway, we did implement the function bodies for the functions that Eq defines, only we defined them in terms of mutual recursion. We said that two instances of Eq are equal if they are not different and they are different if they are not equal. We didn’t have to do this, really, but we did and we’ll see how this helps us soon.
-If we have say class Eq a where and then define a type declaration within that class like (==) :: a -> a -> Bool, then when we examine the type of that function later on, it will have the type of (Eq a) => a -> a -> Bool.
So once we have a class, what can we do with it? Well, not much, really. But once we start making types instances of that class, we start getting some nice functionality. So check out this type:
data TrafficLight = Red | Yellow | Green @@ -796,7 +796,7 @@Typeclasses 102
We had to add a class constraint! With this instance declaration, we say this: we want all types of the form Maybe m to be part of the Eq typeclass, but only those types where the m (so what’s contained inside the Maybe) is also a part of Eq. This is actually how Haskell would derive the instance too.
Most of the times, class constraints in class declarations are used for making a typeclass a subclass of another typeclass and class constraints in instance declarations are used to express requirements about the contents of some type. For instance, here we required the contents of the Maybe to also be part of the Eq typeclass.
When making instances, if you see that a type is used as a concrete type in the type declarations (like the a in a -> a -> Bool), you have to supply type parameters and add parentheses so that you end up with a concrete type.
-Take into account that the type you’re trying to make an instance of will replace the parameter in the class declaration. The a from class Eq a where will be replaced with a real type when you make an instance, so try mentally putting your type into the function type declarations as well. (==) :: Maybe -> Maybe -> Bool doesn’t make much sense but (==) :: (Eq m) => Maybe m -> Maybe m -> Bool does. But this is just something to think about, because == will always have a type of (==) :: (Eq a) => a -> a -> Bool, no matter what instances we make.+Take into account that the type you’re trying to make an instance of will replace the parameter in the class declaration. The a from class Eq a where will be replaced with a real type when you make an instance, so try mentally putting your type into the function type declarations as well. (==) :: Maybe -> Maybe -> Bool doesn’t make much sense but (==) :: (Eq m) => Maybe m -> Maybe m -> Bool does. But this is just something to think about, because == will always have a type of (==) :: (Eq a) => a -> a -> Bool, no matter what instances we make.
Ooh, one more thing, check this out! If you want to see what the instances of a typeclass are, just do :info YourTypeClass in GHCI. So typing :info Num will show which functions the typeclass defines and it will give you a list of the types in the typeclass. :info works for types and type constructors too. If you do :info Maybe, it will show you all the typeclasses that Maybe is an instance of. Also :info can show you the type declaration of a function. I think that’s pretty cool.
A yes-no typeclass
@@ -950,10 +950,10 @@
The Functor typeclass
Well, if we wanted to map one function over both of them, a and b would have to be the same type. I mean, if we tried to map a function that takes a string and returns a string and the b was a string but the a was a number, that wouldn’t really work out. Also, from seeing what fmap’s type would be if it operated only on Either values, we see that the first parameter has to remain the same while the second one can change and the first parameter is actualized by the Left value constructor.
This also goes nicely with our box analogy if we think of the Left part as sort of an empty box with an error message written on the side telling us why it’s empty.
-Maps from Data.Map can also be made a functor because they hold values (or not!). In the case of Map k v, fmap will map a function v -> v' over a map of type Map k v and return a map of type Map k v'.
Maps from Data.Map can also be made a functor because they hold values (or not!). In the case of Map k v, fmap will map a function v -> v' over a map of type Map k v and return a map of type Map k v'.
Note, the ' has no special meaning in types just like it doesn’t have special meaning when naming values. It’s used to denote things that are similar, only slightly changed.
Try figuring out how Map k is made an instance of Functor by yourself!
With the Functor typeclass, we’ve seen how typeclasses can represent pretty cool higher-order concepts. We’ve also had some more practice with partially applying types and making instances. In one of the next chapters, we’ll also take a look at some laws that apply for functors.
-Just one more thing! Functors should obey some laws so that they may have some properties that we can depend on and not think about too much. If we use fmap (+1) over the list [1,2,3,4], we expect the result to be [2,3,4,5] and not its reverse, [5,4,3,2]. If we use fmap (\a -> a) (the identity function, which just returns its parameter) over some list, we expect to get back the same list as a result. For example, if we gave the wrong functor instance to our Tree type, using fmap over a tree where the left subtree of a node only has elements that are smaller than the node and the right subtree only has nodes that are larger than the node might produce a tree where that’s not the case. We’ll go over the functor laws in more detail in one of the next chapters.
Type constructors take other types as parameters to eventually produce concrete types. That kind of reminds me of functions, which take values as parameters to produce values. We’ve seen that type constructors can be partially applied (Either String is a type that takes one type and produces a concrete type, like Either String Int), just like functions can. This is all very interesting indeed. In this section, we’ll take a look at formally defining how types are applied to type constructors, just like we took a look at formally defining how values are applied to functions by using type declarations. You don’t really have to read this section to continue on your magical Haskell quest and if you don’t understand it, don’t worry about it. However, getting this will give you a very thorough understanding of the type system.
diff --git a/docs/modules.html b/docs/modules.html index 1e7e050..22502bd 100644 --- a/docs/modules.html +++ b/docs/modules.html @@ -560,7 +560,7 @@Note: It’s usually better to use folds for this standard list recursion pattern instead of explicitly writing the recursion because they’re easier to read and identify. Everyone knows it’s a fold when they see the foldr call, but it takes some more thinking to read explicit recursion.
ghci> findKey "penny" phoneBook Just "853-2492" diff --git a/docs/recursion.html b/docs/recursion.html index b683b79..873e2e0 100644 --- a/docs/recursion.html +++ b/docs/recursion.html @@ -70,7 +70,7 @@A few more recursive functions
| otherwise = x:replicate' (n-1) x
We used guards here instead of patterns because we’re testing for a boolean condition. If n is less than or equal to 0, return an empty list. Otherwise return a list that has x as the first element and then x replicated n-1 times as the tail. Eventually, the (n-1) part will cause our function to reach the edge condition.
-Note: Num is not a subclass of Ord. This is because not every number type has an ordering, e.g. complex numbers aren’t ordered. So that’s why we have to specify both the Num and Ord class constraints when doing addition or subtraction and also comparison.
Next up, we’ll implement take. It takes a certain number of elements from a list. For instance, take 3 [5,4,3,2,1] will return [5,4,3]. If we try to take 0 or less elements from a list, we get an empty list. Also if we try to take anything from an empty list, we get an empty list. Notice that those are two edge conditions right there. So let’s write that out:
take' :: (Num i, Ord i) => i -> [a] -> [a] diff --git a/docs/starting-out.html b/docs/starting-out.html index dea6b80..8a2856e 100644 --- a/docs/starting-out.html +++ b/docs/starting-out.html @@ -244,7 +244,7 @@An intro to lists
[1,2,3] is actually just syntactic sugar for 1:2:3:[]. [] is an empty list. If we prepend 3 to it, it becomes [3]. If we prepend 2 to that, it becomes [2,3], and so on.
-Note: [], [[]] and[[],[],[]] are all different things. The first one is an empty list, the seconds one is a list that contains one empty list, the third one is a list that contains three empty lists.+Note: [], [[]] and[[],[],[]] are all different things. The first one is an empty list, the seconds one is a list that contains one empty list, the third one is a list that contains three empty lists.
If you want to get an element out of a list by index, use !!. The indices start at 0.
ghci> "Steve Buscemi" !! 6 @@ -505,7 +505,7 @@-Tuples
11 ghci> snd ("Wow", False) FalseNote: these functions operate only on pairs. They won’t work on triples, 4-tuples, 5-tuples, etc. We’ll go over extracting data from tuples in different ways a bit later.+Note: these functions operate only on pairs. They won’t work on triples, 4-tuples, 5-tuples, etc. We’ll go over extracting data from tuples in different ways a bit later.
A cool function that produces a list of pairs: zip. It takes two lists and then zips them together into one list by joining the matching elements into pairs. It’s a really simple function but it has loads of uses. It’s especially useful for when you want to combine two lists in a way or traverse two lists simultaneously. Here’s a demonstration.
ghci> zip [1,2,3,4,5] [5,5,5,5,5] diff --git a/docs/style.css b/docs/style.css index 466bfc8..fe2e5e6 100644 --- a/docs/style.css +++ b/docs/style.css @@ -80,6 +80,9 @@ pre.code { margin-bottom:25px; background-image:url(https://s3.amazonaws.com/lyah/note.jpg); } +.hintbox > p { + margin-bottom:0px; +} .label { white-space:nowrap; font-family:Consolas, "Courier New", monospace; diff --git a/docs/syntax-in-functions.html b/docs/syntax-in-functions.html index 58d5c49..4c8a56a 100644 --- a/docs/syntax-in-functions.html +++ b/docs/syntax-in-functions.html @@ -112,7 +112,7 @@Pattern matching
Should a pattern match fail, it will just move on to the next element.Lists themselves can also be used in pattern matching. You can match with the empty list [] or any pattern that involves : and the empty list. But since [1,2,3] is just syntactic sugar for 1:2:3:[], you can also use the former pattern. A pattern like x:xs will bind the head of the list to x and the rest of it to xs, even if there’s only one element so xs ends up being an empty list.
-Note: The x:xs pattern is used a lot, especially with recursive functions. But patterns that have : in them only match against lists of length 1 or more.+Note: The x:xs pattern is used a lot, especially with recursive functions. But patterns that have : in them only match against lists of length 1 or more.
If you want to bind, say, the first three elements to variables and the rest of the list to another variable, you can use something like x:y:z:zs. It will only match against lists that have three elements or more.
Now that we know how to pattern match against list, let’s make our own implementation of the head function.
@@ -220,7 +220,7 @@-Guards, guards!
ghci> 3 `myCompare` 2 GTNote: Not only can we call functions as infix with backticks, we can also define them using backticks. Sometimes it’s easier to read that way.+Note: Not only can we call functions as infix with backticks, we can also define them using backticks. Sometimes it’s easier to read that way.
Where!?
In the previous section, we defined a density calculator function and responder like this:
diff --git a/docs/types-and-typeclasses.html b/docs/types-and-typeclasses.html index 45c7cfe..8e57826 100644 --- a/docs/types-and-typeclasses.html +++ b/docs/types-and-typeclasses.html @@ -140,7 +140,7 @@-Typeclasses 101
ghci> :t (==) (==) :: (Eq a) => a -> a -> BoolNote: the equality operator, == is a function. So are +, *, -, / and pretty much all operators. If a function is comprised only of special characters, it’s considered an infix function by default. If we want to examine its type, pass it to another function or call it as a prefix function, we have to surround it in parentheses.+Note: the equality operator, == is a function. So are +, *, -, / and pretty much all operators. If a function is comprised only of special characters, it’s considered an infix function by default. If we want to examine its type, pass it to another function or call it as a prefix function, we have to surround it in parentheses.
Interesting. We see a new thing here, the => symbol. Everything before the => symbol is called a class constraint. We can read the previous type declaration like this: the equality function takes any two values that are of the same type and returns a Bool. The type of those two values must be a member of the Eq class (this was the class constraint).
The Eq typeclass provides an interface for testing for equality. Any type where it makes sense to test for equality between two values of that type should be a member of the Eq class. All standard Haskell types except for IO (the type for dealing with input and output) and functions are a part of the Eq typeclass.
The elem function has a type of (Eq a) => a -> [a] -> Bool because it uses == over a list to check whether some value we’re looking for is in it.