James Bowen James Bowen

Learning to Learn Haskell

A month or two ago, we discussed the Intimidation Factor of Haskell. That article focused on why people see Haskell as challenging and why they shouldn't. Today we’ll pick up on some of the threads of that conversation. We'll explore the how of learning Haskell (and other things). We’ll examine some general ideas on learning and discuss how to apply them to programming.

Warren Buffett and Compound Interest

There’s an oft-repeated line on productivity about Warren Buffett. He says he reads at least 500 pages a day, and this is one of the major keys to his success. Knowledge, according to Buffett, is like compound interest. The more you acquire, the more it accumulates and is able to build on itself as you make more connections.

The why of this statement is fantastic. I’ve found it rings true as I explore different topics. I have seen how my knowledge has begun building on itself. And yet, the how is often misunderstood and misinterpreted. This leads people to have a difficult time implementing Buffett's principle.

The simple fact is that the average person does not have time to read 500 pages a day. First, if he reads so much, Warren Buffett is likely an accomplished speed reader, so he needs less time. Second, he is in far more control of his time than most other people due to his wealth. In my current job as a software engineer, I cannot spend a full 80% of my working day on “reading and thinking”. I would have some very unhappy teammates and project managers to deal with.

The average person will see this advice and decide to start reading a ton outside of working hours. They might even succeed in hitting 500 pages a day...for a couple days. Then of course life gets in the way. They won’t have time over a few days to do it, and they’ll drop the habit.

A Better Application

So how do we achieve the compound knowledge effect? The real misinterpretation I find about this advice is this. The key factor in compound interest is time, not average investment. Making small, repeated contributions will have major rewards later on. Of course, big, repeated contributions will have major rewards as well. But if the investment causes us to give up the habit, we'll be worse off over time.

Once we accept this idea, we can apply it to other topics, including Haskell programing. We might be tempted to devote an hour every day to learning some particular Haskell concept. But this is often unsustainable. It is far easier to devote at least 15 minutes a day, or even 10 minutes a day. This will ensure we’re going to continue learning. On any given day, it can be hard to carve out a full hour for something. Your schedule might not allow that contiguous block of time. But you should always be able to find a 15 minute block. This will dramtically lower the barrier of starting each day, so you'll be more likely to succeed.

In keeping with the compound interest principle, progress is momentum based. By committing to 15 minutes a day on a couple different projects, I’ve made a ton of progress. I've accomplished far more than if I had tried to carve out an hour here and there. I was only able to start writing online because I devoted 20 minutes a day to writing. Once I stuck with that for a month, I was in good shape.

Josh Waitzkin and Confronting Difficulties

Another of the most important ideas about learning I’ve encountered comes from The Art of Learning, by Josh Waitzkin. He is a former chess prodigy and grandmaster turned world-champion martial artist. He describes a story that was all-too familiar to me as a fellow childhood chess player. He saw many young players with a lot of potential. They would beat everyone around them at their school or local chess club. But they never brought themselves to face stronger opposition. As a result, they ended up not improving, and ultimately quit chess. They were so invested in the idea of winning every game that losing to anyone was too much of a blow to their pride.

If we focus on our egos too much, we’ll be afraid of appearing weak. The causes us to avoid confronting the areas of our knowledge where we actually are weak. These are exactly the areas we need to strengthen! If we never address these areas, we’ll never improve, and we won't be able to beat big challenges.

Confronting Haskell

So how does this affect learning Haskell, or programming in general? After all, programming is not a competitive game. And yet, there are still ways in which this mentality can hurt us. We might stay away from a particular topic because it seems difficult. We're concerned that we'll try to learn it and fail. And we worry this failure will reveal how peculiarly unfit we are to be Haskell developers. Worse, we're afraid to ask other programmers for help. What if they look down on us for our lack of knowledge?

I have three main responses to this. First, I'll repeat a note from the intimidation article. A topic is infinitely more intimidating when you know nothing about it. Once you know even the most basic definitions, you have a reasonable idea of what you're missing. Get as basic an idea of it as you can and write it down in plain English. You might not know the subject. But it will no longer be an "unknown-unknown".

Second, who cares if you put in effort toward learning something and fail? Try again! It can take several iterations of learning on a single topic before you understand it. It took me at least three tries before I understood monads.

Finally, the very people we are afraid to admit our weaknesses to are the same people who can actually help us overcome these weakness. Better yet, they are often more than happy to do so! This involves getting over our primal fears of appearing inferior and being rejected by others. This is difficult but not impossible.

Conclusion

So remember the key lessons here. Focus a little bit at first. Don’t commit to learning more than 15 minutes a day, and pick a project with clear progress. Keep momentum going by working at something every day. Don't worry if a concept seems difficult. It's OK to take several tries at learning something. And above all, never be afraid to ask for help.

So what are you waiting for? If you've always wanted to learn Haskell but never have, download our Getting Started Checklist to start your journey!

Have you done a little Haskell, but still don't understand some functional programming concepts? Check out our free Recursion Workbook to learn about recursion, higher order functions, and try some practice problems!

Read More
James Bowen James Bowen

Interview with Doug Beardsley!

This week on Monday Morning Haskell, we have an exciting change of pace! I recently met with Doug Beardsley, a Haskell engineer at Takt. He was kind enough to agree to an interview with me. We discussed a number of topics, including learning Haskell, his work at Takt, and his view of the Haskell community as a whole.

You can find out more about him and his projects on his Github page. One of the more interesting things I saw there was his monad challenges repository. If you’ve read through the Monday Morning Haskell monad series and want some more hands on experience, I encourage you to try these challenges!

Perhaps the most striking response I heard from Doug was his answer about how welcoming and helpful the Haskell community is. Haskellers are always eager to answer newcomers’ questions! So if you haven’t written any Haskell yet, I encourage you to look through our Getting Started Checklist and take the first steps on your Haskell journey!

Without further ado, let’s get on to the interview!

Interview

Let’s start with some information about yourself. How long have been a software engineer? How long have you been using Haskell?

I started programming back in 1990ish, and I was pretty young. It wasn’t really software engineering level programming back then obviously, but I just immediately fell in love with it. I then learned C and programmed with it all through high school. And so I knew I wanted to be a computer science major in college. I went through that, graduated, started programming in the early 2000’s in industry, first in the defense industry for five/six years. Then I got a job programming Haskell. I moved to New York for that, and I’ve been doing Haskell pretty much full time ever since.

How long ago was that?

I started programming in Haskell professionally in 2010. I didn’t jump straight into Haskell professionally from zero. I think I started learning Haskell around 2006, maybe 2007.

So what Haskell concept was the hardest for you to learn?

Wow that’s a good question. I feel like with everything, there’s a spectrum. As you evolve, new things become harder. I definitely had a lot of trouble with monads early on. I feel like you don’t learn them at any one time. You just use them enough, and then you gradually build up a working knowledge of the tools and the APIs that are available around Control.Monad. I read through that so many times. If, like me, you don’t have a photographic memory, you just gotta keep going back through refreshing your memory, and eventually the patterns just kind of sink in.

I don’t know if I would say that was the hardest concept though. I feel like I can still use a much better understanding of say, profunctors, or some of the more obscure things that are going on in the lens package. But I don’t know. I kinda don’t like questions like “What is the superlative of something”, because it depends on the day really.

That makes sense. What advice would you have for someone who has tried to learn Haskell and has either given up, or is having a really tough time? Are there any 80/20 ideas or important easy wins when learning Haskell?

The biggest thing for me is you need to have intrinsic motivation. So in my case, I had an app that I wanted to build, and I had been building it in Drupal, with PHP, and that just fell flat on its face very quickly. I really wanted to build the app, for myself, not for a startup or to make money; I wanted it for myself. So I had a very high level of intrinsic motivation.

I wasn’t really a big fan of web programming back then, I was more of a back-end kind of programmer, and I thought “I want to learn a new language, Haskell seems like one of the more challenging ones that would stretch me more, maybe it’ll make web programming interesting”. As someone once said on IRC, “a spoonful of Haskell makes the web programming go down”. Which I think is a very apt description of my case, so I just dove in. I started working on it. It was a way bigger project than I should have been working on as a Haskell novice.

But I think that was good. You need to be working on something you think is maybe a little bit beyond you. And if you also have that intrinsic motivation taken care of, then it almost solves itself. You’re going to bang your head against a wall until you beat it. I’ve heard a friend of mine is really big into natural language learning and says you just have to find some way of getting over this incredible hurdle because it’s so hard. You just have to make yourself do it. For Haskell I think it’s a very similarly challenging endeavor, and you really have to have intrinsic motivation.

So the WHY is almost more important than any particular WHAT?

Yea I think so. I just started out with Project Euler problems, which is something I hear a lot of people do. And OK great, it’s like “Oh, I’ll spend some time working on this problem.” You spend an hour on it, and you don’t learn a whole lot of Haskell. You learn a little bit of the basic syntax, but not a lot that’s useful in terms of real world development. And there’s not a whole lot of intrinsic motivation there. I suppose maybe you could say, “Oh I want to finish all the problems.” But that’s not really a question of learning Haskell. That’s more a question of learning math than any particular programming language.

And so I think you need a project that’s going to push you into a fair number of realistic, real world situations. In my case, a web app was suitable. Web apps do pretty well because they involve a fair number of pieces of technology and teach you the software development process. But yea, it’s not so much the “what”, for sure. It’s just, you gotta have a reason that lights a fire under you.

I guess my own experience is a little bit like that. I had this product idea and I had done simple versions of a couple of the pieces of that idea in college in a class where I first learned Haskell. And I thought, well I could just use Haskell for this whole project. So then I put the whole project together and that’s what started getting me towards learning the more practical side of things like Stack. It also got me to learn more about monads and that sort of stuff.

So having worked on Haskell in industry, what is your take on the idea widely held in many parts of the programming community that Haskell isn’t suitable for production use? Obviously you’re very opinionated here.

Yea it just couldn’t be farther from the truth. There are plenty of people in the world who are doing production stuff in Haskell and I think that proves it. For instance, JanRain, I think, has used Snap [a Haskell library] for years. I assume they still are, but I used to be able to say “hey, when you go to ladygaga.com, you’re hitting a snap server”. And that’s real stuff. That’s solving real problems in the real world.

We don’t have yet the Google or the Facebook or something of that scale. Though Facebook actually is using Haskell (on the side), just not with their forward user facing stuff. So we don’t have a huge example to point at, but there’s certainly people using Haskell every day in production. So it’s just straight up not true that it can’t be done.

So what is the biggest challenge you’ve had using Haskell on an industry project?

There’s a couple of things that jump out. One was a some kind of machine learning or more numerical algorithm I was working on. It might’ve been a hidden markov model or something with a lot of math going on. And I had a fair amount of trouble getting that right. This was quite a while back, so I was less experienced than I am now.

I think part of the challenge there was not having the right abstractions for multi-dimensional matrix math. And I think Haskell is still not really in a fantastic state there. You can do it, you can accomplish the matrix math with say, HMatrix, and I was able to. But in terms of wrangling dimensions and units and stuff like that, I probably didn’t know to take advantage of some of the things that are available now.

There were definitely some challenges there and it seemed like a very difficult thing to get right. Although I suspect it’s difficult to get right no matter what language you’re using. So it may be the case that Haskell just doesn’t have as much of an advantage there until we get some really phenomenal matrix libraries that could help us solve that problem a lot more nicely.

Another situation where I had frustrations was with an ETL [Extract-Transform-Load == migrating a legacy system] that I was working on and it was so miserable to get right. I was just doing it the naive way bashing through the problem head-on using the IO monad. I was trying to abstract things just using functions, type classes, and whatever other functional techniques I knew about.

Maybe what I should have done is gone back and looked at some kind of a more fundamental approach, perhaps an EDSL [Embedded Domain Specific Language] with say, a GADT [Generalized Algebraic Data Type] and specifying operations representing the structure of what I was trying to do. Then I would have an interpreter that would interpret that in a test type of context where I might be counting different operations that are happening in all manner of checks that you might want to do. Then I would have another interpreter that interprets it in IO and you can kind of test these things in a more structured way.

I think there were a lot of things I could have done better in that project, but maybe as happens in so many projects, there was time pressure. And if that’s the background situation, then you’re less likely to take your time and look for a really good solution to that problem.

Like you said, there’s often a brute-force solution and then a more elegant solution in most software projects. But if you’re operating under major time pressures like so many projects are, it’s hard to find it, especially in Haskell.

I feel like if I had maybe had prior experience with using some of those more elegant solutions, I would have been more likely to jump to them. And this is maybe an obstacle in a more general way. Learning to find a reason to use some of these things, so then you’ll be able to use them in the real world.

Yea that’s a very interesting problem.

To what extent do you think abstract math is necessary to understand Haskell, and to what extent do you think it is helpful? Is there a particular instance where you’ve used higher math to solve a Haskell problem?

I don’t think it is necessary at all to be productive with Haskell. I wouldn’t consider myself super strong in category theory. I took an abstract algebra class in college, so I’m not at the bottom of the barrel, but I don’t think it was super significant to learning Haskell. Things definitely come up. You’ve got all kinds of examples of places where people have leveraged abstract math. But I feel like the knowledge of those structures might actually be part of the answer to the problem we were talking about a second ago.

If you are adequately motivated and you know about this structure somehow, you can use it. The Lens library comes to mind as one. You may not know how profunctors or prisms or whatever other concepts there are, but you know about the lens library, so you can learn about concepts that way, in a library/problem directed way. Maybe knowing the structures would help you know what to use for a more elegant solution to a problem.

But at the same time, when I have studied some category theory concepts, it’s not obvious at all how I’m going to apply them to programming, so we need to do much better as a community at bridging this gap between theory and practice. I think we already do better than any community out there though. Which is good because Haskell does have this reputation of being a very theoretical language. I think we do a great job, but I think we can still improve a lot.

Yea I’m wondering...is there even a theoretical PHP for that community to bridge?

Yea maybe that’s an oxy-moron...

What’s your favorite miscellaneous Haskell library?

Boy, that’s an interesting question. One thing that pops to mind is libraries that represent paths more semantically. So you can concatenate them and manipulate them without having to parse and split on slashes and things like that. I feel like I should have an answer to this question.

Perhaps it’s just far enough back in my brain that I’m not thinking of it right now. But yea, paths are something I encountered a while back. When you really need it, they’re a really cool thing to have...being able to target absolute paths and relative paths. There’s perhaps a whole algebra of paths there that some libraries can give you which is interesting.

What is your least favorite thing about Haskell?

I would say it’s the fact that sometimes we DON’T have referential transparency. The thing that comes to mind that I’ve encountered just recently was that I wanted to just make something generic over a particular prism but I couldn’t, because each of the prisms I would have ended up using returned a different value. So it needed to be more monomorphic than I would have ideally wanted.

So instead of having the absolute right level of abstraction, I had to add a level of repetition to get an Int64 or whatever I was prism-ing out to. We do have referential transparency but occasionally there’s some of these real-world type system restrictions that force us to be more monomorphic than we would like.

So let’s talk a little bit about Takt, where you work. I guess first, do you want to give a quick elevator pitch about Takt? What do you do, what problems do you solve, both the company as a whole, and you yourself?

The company as a whole has lots of very interesting problems. We’re a fairly large-scale data processing/data-science company automating for clients and making intelligent decisions to the point of trying to create a more personalized experience for our clients’ customers. There’s tons of interesting problems to solve there. The one that I’ve been working on most recently is just building a web front-end. And so we’re using Haskell and GHCJS and the Reflex FRP library to build UIs that are robust and easy to refactor and be confident about. So that’s my area of focus.

The company is just a fantastic place to work. It’s really refreshing to have a group of people who are unified in the pursuit of Haskell as the right solution to many of our problems. There are some cases where maybe it’s not the right solution. Maybe you just need some quick and dirty scripts on a server. Especially if you don’t have GHC on that server. So there’s plenty of real world situations where you end up reaching for something else. But as often as we can we like to reach for Haskell, and that’s a really fun environment to be involved in.

In what ways do you think Haskell is uniquely suited for solving Takt’s problems?

I feel that Haskell is uniquely suited for solving ALL the world’s software problems! I truly believe that Haskell or at least the ideas that Haskell is based on are the future of software. The future of more reliable, robust software. And so I feel like it’s going to the future for all of Takt’s software problems.

What is one piece of advice for someone you might have for someone who might want to apply to Takt?

Well if you’re not a Haskell programmer, you should learn some Haskell for sure. I even wrote a blog post awhile back about how to get a Haskell job. You can’t really expect people to hire you for a language that is as different from the mainstream as Haskell is without some prior experience. You can’t expect a company to hire you as any kind of programmer without having some prior experience programming. So I think that’s a pretty natural thing. It’s maybe frustrating for some people who have trouble finding the time to invest in learning Haskell, but it’s kind of a reality.

For Haskell programmers out there who are interested in working at Takt, talk to us! We’d love to hear from you. If you don’t want to just put your resume on a faceless website, you can contact me and open a conversation that way. It’s really nice when candidates have public work that we can look at and get an idea of what kind of Haskell skills you have. We don’t require that because there are plenty of candidates out there that don’t have that for whatever reason, but it can’t hurt.

We’re in the business of trying to reduce risk, and the more we can know about you, the better. Open source contributions are really helpful there. And the more visible that particular project is, the more it’s going to be significant in making us take a look at you and make us excited to talk to you.

Awesome. So let’s finish up with a couple questions about the broader community. What’s your take on the Haskell community at large? What are its strengths, weaknesses, etc..

I think we’re super strong in the area of just being willing to help newcomers. Super friendly. I can’t possibly count how many hours I’ve spent getting help online from people who were just giving me their time just to help me learn some abstract concept where I was taking forever to catch on. It’s just a fantastically friendly and talented community.

I’ve had the opportunity to look at jobs where we were advertised in both a Haskell forum and a non-Haskell forum and the Haskell candidates were just an order of magnitude better and more promising. Now I can’t say that proves anything, because maybe there were other differences about the way those two postings were done that would bias it somehow. But it seems to suggest that it’s a phenomenal filter. If you’re involved in the Haskell community, then you have a lot of intrinsic motivation like we talked about earlier.

You just can’t get to a working knowledge of Haskell by sitting around, being lazy, and watching TV. You gotta actually work for it. Anything in life that requires people to really work for it is going to be a strong predictor of success or maybe the ability to be successful, or a lot of attributes that are aligned and I think are valuable in potential employees.

Very interesting. What changes do you see happening in the Haskell community or the language itself in the next year?

Hmmm that’s a good question. Well I hope to be able to say in the next year or two that Takt is contributing some new libraries and new points in the ecosystem that are missing. Hopefully we can get some more companies using Haskell for adoption. I think there’s some more work recently to make Haskell more tractable for newcomers, and I think that’s fantastic. That’s an area where we’ve struggled a little bit because we are so different from the mainstream. And I really look forward to seeing things grow.

Awesome! That’s all the questions I have. Thanks so much for doing this interview.

It’s a pleasure!

Wrap Up

Hopefully our interview helped give you a better idea of what it’s like working with Haskell in industry. And as Doug mentioned, the Haskell community is very welcoming to newcomers! If you’ve never tried Haskell before, check out our Getting Started Checklist. It’ll show you what tools you need to start writing your first lines of Haskell.

If you’ve done a little Haskell already but want some more practice, you should try our free Recursion Workbook. It has lots of material on recursion and also includes 10 practice problems!

Be sure to check out the Monday Morning Haskell Blog next week. We’ll have a new piece on how we can use Haskell compiled nature to help drive our development and improve our productivity!

Read More
James Bowen James Bowen

Obey the (Type) Laws!

We should now have a decent grasp on functors, applicative functors, and monads. Be sure to check these articles out if you need a refresher! Now we understand the concepts, so it’s time to learn the laws around them.

Remember Haskell represents each of these mathematical classes by a type class. Each of these type classes has one or two main functions. So, as long as we implement those functions and it type checks, we have a new functor/applicative/monad, right?

Well not quite. Yes, your program will compile and you’ll be able to use the instances. But this doesn't mean your instances follow the mathematical constructs. If they don't, your instances won't fulfill other programmers’ expectations. Each type class has its own “laws”. For instance, let's think back to the GovDirectory type we created in the functor article. Suppose we made a different functor instance than we had there:

data GovDirectory a = GovDirectory {
  mayor :: a,
  interimMayor :: Maybe a,
  cabinet :: Map String a,
  councilMembers :: [a]
}

instance Functor GovDirectory where
  fmap f oldDirectory = GovDirectory {
    mayor = f (mayor oldDirectory),
    interimMayor = Nothing,
    cabinet = f <$> cabinet oldDirectory,
    councilMembers = f <$> councilMembers oldDirectory
  }

As we’ll see, this would violate one of the functor laws. In this case it would not be a true functor. Its behavior would confuse any other programmer trying to use it. We should take care to make sure that our instances make sense. Once you get a feel for these type classes, the likelihood is that the instances you’ll create follow the laws. So don’t sweat it if a few of these are confusing. This article will be very math-y, and we won’t dwell too much on the concepts. You can understand and use these classes without knowing these laws by heart. So without further ado, let’s dive into the laws!

Functor Laws

There are two functor laws. The first is an identity law. We’ll see some variation of this idea for each of the type classes. Remember how fmap "maps" a function over our container. If we map the identity function over a container, the result should be the same container object:

fmap id = id

In other words, our functor should not be applying any extra changes or side effects. It should only apply the function. The second law is a composition law. It states our functor implementation should not break the composition of functions:

fmap (g . f) = fmap g . fmap f

-- For reference, remember the type of the composition operator:
(.) :: (b -> c) -> (a -> b) -> (a -> c)

On one side we compose two functions, and map the resulting function over our container. On the other side, we map the first function, get the result, and map the second function over it. The functor composition law states these outcomes should be identical. This sounds complex. But you don't need to worry about it much. If you break the composition law in Haskell, you'll also likely break the identity law.

Those are the only two laws for functors! So let's move on to applicative functors.

Applicative Laws

Applicative functors are a little more complicated. They have four different laws. The first is easy though. It's another simple identity law. It says:

pure id <*> v = v

On the left side, we wrap the identity function. Then we apply it over our container. The applicative identity law states this should result in an identical object. Simple enough.

The second law is the homomorphism law. Suppose we wrap a function and an object in pure. We can then apply the wrapped function over the wrapped object. Of course, we could also apply the normal function over the normal object, and THEN wrap it in pure. The homomorphism law states these results should be the same.

pure f <*> pure x = pure (f x)

We should see a distinct pattern here. The overriding theme of almost all these laws is that our type classes are containers. The type class function should not have any side effects. All they should do is facilitate the wrapping, unwrapping, and transformation of data.

The third law is the interchange law. It’s a little more complicated, so don’t sweat it too much. It states that the order that we wrap things shouldn’t matter. One on side, we apply any applicative over a pure wrapped object. On the other side, first we wrap a function applying the object as an argument. Then we apply this to the first applicative. These should be the same.

u <*> pure y = pure ($ y) <*> u

The final applicative law mimics the second functor law. It is a composition law. It states that function composition holds across applications within the functor:

pure (.) <*> u <*> v <*> w = u <*> (v <*> w)

The sheer number of laws here can be a little overwhelming. Remember, the instances you make will probably follow the laws! Let’s move on to our final example: monads.

Monad Laws

Monads have three laws. The first two are simple identity laws, like our other classes have had:

return a >>= f = f
m >>= return = m

These are the left and right identities. They state effectively that the only thing the return function is allowed to do is to wrap the object (sound familiar?). It cannot manipulate the data in any way. Our main takeaway from these is that the following code samples are equivalent:

func1 :: IO String
func1 = do
  str <- getLine
  return str

func2 :: IO String
func2 = getLine

The third law is a bit more interesting. It tells us that associativity holds within monads:

(m >>= f) >>= g = m >>= (\x -> f x >>= g)

But we see this third law has a parallel structure to the other composition laws. In the first case, we apply two functions in two steps. In the second case, we compose the functions first, and THEN apply the result. These should be the same.

So in summary, there are two main ideas from all the laws. First, identity should be preserve over wrapper functions, like pure and return. Second, function composition should hold across our structures.

Checking the Laws

As I stated before, most of the instances that you come up with will naturally follow these rules. As you get more experience with the different type classes, this will be even more true. Yet, it also pays to be sure. Haskell has an excellent tool for verifying your instances pass a certain law.

This utility is QuickCheck. It can take any a certain rule, generate many different test cases on the rule, and verify the rule holds. In this section, we’ll run a few tests on our GovDirectory functor instance. We'll see how QuickCheck proves its initial failure, and ultimate success. First we need to implement the Arbitrary type class over our type. We can do this a long as the inner type is also Arbitrary, such as a built-in string type. Then we’ll use all the other Arbitrary instances that exist over our inner types:

instance Arbitrary a => Arbitrary (GovDirectory a) where
  arbitrary = do
    m <- arbitrary
    im <- arbitrary
    cab <- arbitrary
    cm <- arbitrary
    return $ GovDirectory
      { mayor = m
      , interimMayor = im
      , cabinet = cab
      , councilMembers = cm }

Once you have done that, you can write a test case over a particular rule. In this case, we check the identity function for functors:

main :: IO ()
main = quickCheck govDirectoryFunctorCheck

govDirectoryFunctorCheck :: GovDirectory String -> Bool
govDirectoryFunctorCheck gd = fmap id gd == gd

Now let’s test this on the faulty instance we used above. We can see that a particular test will fail:

*** Failed! Falsifiable (after 2 tests):
GovDirectory {mayor = "", interimMayor = Just "\156", cabinet = fromList [("","")], councilMembers = []}

It specifies for us an arbitrary instance that failed the test. Now suppose we correct the instance:

interimMayor = f <$> (interimMayor oldDirectory),

We’ll see the tests pass!

+++ OK, passed 100 tests.

Summary

We've discussed three major type classes: functors, applicative functors, and monads. They all have particular laws their instances should follow. Other programmers who use your code will expect any instances you make to follow these laws. Once you are familiar with the types, you will likely create instances that follow the laws. But if you are unsure, you can use the QuickCheck utility to verify them.

This concludes our series on monads! You should now have all the tools you need to start using them in practice. Remember that they are a difficult concept, and you'll likely have to review them a couple times. But eventually, you'll understand them!

If you're now inspired to get started with Haskell, make sure to check out our free Getting Started Checklist! It'll help kickstart your Haskell experience by helping you through the download process and making your first project with Stack!

If you're up for a bigger challenge, you should get our Recursion Workbook. It's also free! It has a couple chapters of material on recursion and higher order functions. It also has 10 practice problems for you to try out!

Read More
James Bowen James Bowen

Making Sense of Multiple Monads

We’ve recently how the maybe monad has helped us avoid triangle of doom code patterns. Without it, we had to check each function call for success. However, the examples we looked at were all pure code examples. Consider this:

main :: IO
main = do
  maybeUserName <- readUserName
  case maybeUserName of
    Nothing -> print “Invalid user name!”
    Just (uName) -> do
      maybeEmail <- readEmail
      case maybeEmail of
        Nothing -> print “Invalid email!”
        Just (email) -> do
          maybePassword <- readPassword
          Case maybePassword of
            Nothing -> print “Invalid Password”
            Just password -> login uName email password

readUserName :: IO (Maybe String)
readUserName = do
  str <- getLIne
  if length str > 5
    then return $ Just str
    else return Nothing

readEmail :: IO (Maybe String)
...

readPassword :: IO (Maybe String)
...

login :: String -> String -> String -> IO ()
...

In this example, all our potentially problematic code takes place within the IO monad. How can we use the Maybe monad when we’re already in another monad?

Monad Transformers

Luckily, we can get the desired behavior by using monad transformers to combine monads. In this example, we’ll wrap the IO actions within a transformer called MaybeT.

A monad transformer is fundamentally a wrapper type. It is generally parameterized by another monadic type. You can then run actions from the inner monad, while adding your own customized behavior for combining actions in this new monad. The common transformers add T to the end of an existing monad. Here’s the definition of MaybeT:

newtype MaybeT m a = MaybeT { runMaybeT :: m (Maybe a) }

instance (Monad m) => Monad (MaybeT m) where
    return = lift . return
    x >>= f = MaybeT $ do
        v <- runMaybeT x
        case v of
            Nothing -> return Nothing
            Just y  -> runMaybeT (f y)

So MaybeT itself is simply a newtype. It in turn contains a wrapper around a Maybe value. If the type m is a monad, we can also make a monad out of MaybeT.

Let’s consider our example. We want to use MaybeT to wrap the IO monad, so we can run IO actions. This means our new monad is MaybeT IO. Our three helper functions all return strings, so they each get the type MaybeT IO String. To convert the old IO code into the MaybeT monad, all we need to do is wrap the IO action in the MaybeT constructor.

readUserName :: MaybeT IO String
readUserName = MaybeT $ do
  str <- getLIne
  if length str > 5
    then return $ Just str
    else return Nothing

readEmail :: MaybeT IO String
...

readPassword :: MaybeT IO String
...

Now we can wrap all three of these calls into a single monadic action, and do a single pattern match to get the results. We’ll use the runMaybeT function to unwrap the Maybe value from the MaybeT:

main :: IO ()
main = do
  maybeCreds <- runMaybeT $ do
    usr <- readUserName
    email <- readEmail
    pass <- readPassword
    return (usr, email, pass)
  case maybeCreds of
    Nothing -> print "Couldn't login!"
    Just (u, e, p) -> login u e p

And this new code will have the proper short-circuiting behavior of the Maybe monad! If any of the read functions fail, our code will immediately return Nothing.

Adding More Layers

Here’s the best part about monad transformers. Since our newly created type is a monad itself, we can wrap it inside another transformer! Pretty much all common monads have transformer types in the same way the MaybeT is a transformer for the ordinary Maybe monad.

For a quick example, suppose we had an Env type containing some user information. We could wrap this environment in a Reader. However, we want to still have access to IO functionality, so we’ll use the ReaderT transformer. Then we can wrap the result in MaybeT transformer.

type Env = (Maybe String, Maybe String, Maybe String)

readUserName :: MaybeT (ReaderT Env IO) String
readUserName = MaybeT $ do
  (maybeOldUser, _, _) <- ask
  case maybeOldUser of
    Just str -> return str
    Nothing -> do
      -- lift allows normal IO functions from inside ReaderT Env IO!
      input <- lift getLine
      if length input > 5
        then return (Just input)
        else return Nothing

Notice we had to use lift to run the IO function getLine. In a monad transformer, the lift function allows you to run actions in the underlying monad. So using lift in the ReaderT Env IO action allows IO functions. Within a MaybeT (ReaderT Env IO) function, calling lift would allow you to run a Reader function. We don’t need this above since the bulk of the code lies in Reader actions wrapped by the MaybeT constructor.

To understand the concept of lifting, think of your monad layer as a stack. When you have a ReaderT Env IO action, imagine a Reader Env monad on top of the IO monad. An IO action exists on the bottom layer. So to run it from the upper layer, you need to lift it up. If your stack is more than two layers, you can lift multiple times. Calling lift twice from the MaybeT (ReaderT Env IO) monad will allow you to call IO functions.

It’s inconvenient to have to know how many times to call lift to get to a particular level of the chain. Thus helper functions are frequently used for this. Additionally, since monad transformers can run several layers deep, the types can get complicated, so it is typical to use type synonyms liberally.

type TripleMonad a = MaybeT (ReaderT Env IO) a

performReader :: ReaderT Env IO a -> TripleMonad a
performReader = lift

performIO :: IO a -> TripleMonad a
performIO = lift . lift

Typeclasses

As a similar idea, there are some typeclasses which allow you to make certain assumptions about the monad stack below. For instance, you often don’t care what the exact stack is, but you just need IO to exist somewhere on the stack. This is the purpose of the MonadIO typeclass:

class (Monad m) => MonadIO m where
  liftIO :: IO a -> m a

We can use this behavior to get a function to print even when we don’t know its exact monad:

debugFunc :: (MonadIO m) => String -> m a
debugFunc input = do
  liftIO $ print “Interpreting Input: “ ++ input
  …

One final note: You cannot, in general, wrap another monad with the IO monad using a transformer. You can, however, make the other monadic value the return type of an IO action.

func :: IO (Maybe String)
-- This type makes sense

func2 :: IO_T (ReaderT Env (Maybe)) string
-- This does not exist

Summary

Monad Transformers allow us to wrap monads within other monads. All of the basic built-in monads have transformer types. We name these types by adding T to the end of the name, like MaybeT. Monad transformers let us get useful behavior from all the different monads on our stack. The lift function allows us to run functions within monads further down the stack.

Monad transformers are extremely important when trying to write meaningful Haskell code. If you want to get started with Haskell, be sure to check out our free checklist for Haskell tools.

Want to practice some Haskell skills, but aren’t ready for monads? You can also take a look at our recursion workbook (it’s also free!). It has two chapters of content on recursion and higher order functions, as well as 10 practice problems.

Stay tuned, because next week we will complete our discussion of our abstract wrapper types (functors, applicatives, monads) by exploring the laws governing their behavior.

Read More
James Bowen James Bowen

The Monadic State of Mind

In this article, we’re going to describe one final monad. The State monad is among the most common and most useful. We’ve seen the Reader monad, which allows you to have a common global value among several functions. We also examined the Writer monad, which allows you to keep adding to a particular monoid value. The State monad combines these ideas to give us a full read-write state. We’ll see how this allows our code to have many of the qualities of other programming languages that Haskell seems to lack.

Motivating Example: Monopoly

For this article, we’ll use a simple model for a Monopoly-like game. The main object is the GameState data type containing several important pieces of information.

data GameState = GameState
  { players :: [Player]
  , chanceDeck :: [GameCard]
  , properties :: Map Property PropertyState
  , piecePositions :: Map Player Property 
  . generator :: StdGen }

data PropertyState = Unowned | Owned Player

data ChanceCard = …
data Player = …
data BoardPostion = …
data GameAction = ...

Let’s think at a high level about how some of our game functions would work. We could, for instance, have a function for rolling the dice. This would output a number and alter our game’s number generator. We would then make a move based on the dice output and the current player. This would change the piece positions in the board state as well as leaving us with an output action to resolve (like drawing a card, or taking action on a property).

Buying a property also changes the board’s state. Drawing a chance card would update the state of the deck while returning us a GameCard to resolve. We see a common pattern here among the different actions. Almost all of them will update GameState in some way, and some of them will have an additional piece of output we’ll want to use.

The State Monad

This is exactly the situation the State monad deals with. The State monad wraps computations in the context of reading and modifying a global state object. This context chains two operations together by determining what the state should be after the first operation, and then resolving the second operation with the new state.

It is parameterized by a single type parameter s, the state type in use. So just like the Reader has a single type we read from, the State has a single type we can both read from and write to.

The two main functions we’ll use within the State monad with are get and put. They do exactly what you expect they’d do. The get function works much like the ask function of the reader monad, retrieving our state value. Meanwhile, put works similarly to tell in the Writer monad, where we’ll pass an updated state. Finally we observe there will still be a final return type on each expression in State, just as there is in any other monad. Thus our different function types will look like this for a return type of a:

State GameState a

Our Monopoly Functions

Now we can examine some of the different functions mentioned above and determine their types. We have for instance, rolling the dice:

rollDice :: State GameState Int
rollDice = do
  currentState <- get
  let gen = generator currentState
  let (d1, gen') = randomR (1,6) gen
  let (d2, gen'') = randomR (1,6) gen'
  put (currentState { generator = gen'' } )
  return (d1 + d2)

This outputs an Int to us, and modifies the random number generator stored in our state! Now we also have the function making a move:

movePiece :: Player -> Int -> State GameState Property
movePiece player roll = do
  currentState <- get
  let currentPositions = piecePositions currentState
  let currentPos = fromJust (M.lookup player currentPositions)
  let next = nextProperty currentPos roll
  let newMap = M.insert player next currentPositions
  put (currentState { piecePositions = newMap } ) 
  return next

nextProperty :: Property -> Int -> Property
...

This will give us the output of the new property we landed on, while also modifying the board with our new position of our piece. Based on the resulting position, we might take different actions, like drawing a chance card:

drawChance :: State GameState ChanceCard
drawChance = do
  currentState <- get
  let (fstCard : deck) = chanceDeck currentState
  put (currentState { chanceDeck = deck } )
  return fstCard

As we said above, this will modify the pile of available cards in the chance pile. There are other stateful functions we could describe, such as resolving a property purchase, or paying rent to another player. These would also exist within the state monad.

buyProperty :: Player -> Property -> State GameState ()
…

payRent :: Player -> Property -> State GameState ()
...

So finally, we can combine all these functions together with do-syntax, and it actually looks quite clean! We don’t need to worry about the side effects. The different monadic functions handle them. Here’s a sample of what your function might look like to play one turn of the game:

resolveTurn :: State GameState ()
resolveTurn = do
  currentState <- get
  let playerToMove = currentPlayer currentState 
  roll <- rollDice
  newProperty <- movePiece playerToMove roll
  action <- actionForProperty playerToMove newProperty
  resolveAction action
  switchNextPlayer
  return ()

Obviously, we haven’t described all these functions, but the general idea should be clear. They would all exist within the state monad.

State, IO, and Other Languages

When thinking about Haskell, it is often seen as a restriction that we can’t have global variables we can modify, like you could with Java class variables. However, we see now this isn’t true. We could have a data type with exactly the same functionality as a Java class, where many functions can modify the global state of the class object using the State monad.

The difference is in Haskell we simply put a label on these types of functions. We don’t allow it to happen for free. We want to know when side effects can potentially happen, because knowing when they can happen makes our code easier to reason about. In a Java class, many of the methods won’t actually need to modify the state. But they could, which makes it harder to debug them. In Haskell we can simply make these pure functions, and our code will be simpler.

IO is the same way. It’s not like we can’t perform IO in Haskell. Instead, we want to label the areas where we can, to increase our certainty about the areas where we don’t need to. When we know part of our code cannot communicate with the outside world, we can be far more certain of its behavior.

Summary

The State monad allows us to have a global readable and writable state. It gives Haskell exactly the kind of flexibility you expect to find in any other programming language. But by separating stateful code from our pure code, our pure code becomes much easier to reason about.

Since we have so many monads under our belts now, the next step is to know how to combine them. Next week we’ll talk about monad transformers, and how those enable us to use multiple monadic functionalities together!

If this has piqued your curiosity for Haskell but you don’t know where to begin, checkout out our checklist to learn more!

If this has inspired you to try out some Haskell code, be sure to try out our free workbook on recursion and higher order function. It includes 10 practice problems so you can hone your skills!

Read More
James Bowen James Bowen

How to Read and Write (with Monads!)

So last week we discussed what a monad is. It isn’t some scary thing only wizards with arcane knowledge of category theory can understand. It’s just a type class with a couple functions describing a particular context. These functions, when used properly, can dramatically expand what we can do while keeping our code purely functional.

We haven’t gone over all the “laws” these functions need to follow. But if we explore enough examples, we’ll have an intuitive grasp of what should happen. We saw some simple examples last time with the Maybe, Either, and IO monads. In this article, we will look at the Reader and Writer monads.

Global Variables (or a lack thereof)

In Haskell, our code is generally “pure”, meaning functions can only interact with the arguments passed to them. This effectively means we cannot have global variables. We can have global expressions, but these are fixed at compile time. If user behavior might change them, we have to wrap them in the IO monad, which means they can’t be used from pure code.

Consider this example where we might want to have an Environment containing different parameters as a global variable. However, we might have to load these from a config file or a command line interface, which requires the IO monad.

main :: IO ()
main = do
  env <- loadEnv
  let str = func1 env
  print str

data Environment = Environment
  { param1 :: String
  , param2 :: String
  , param3 :: String }

loadEnv :: IO Environment
loadEnv = …

func1 :: Environment -> String
func1 env = “Result: “ ++ (show (func2 env))

func2 :: Environment -> Int
func2 env = 2 + floor (func3 env)

func3 :: Environment -> Float
func3 env = … -- Some calculation based on the environment

The only function actually using the environment is func3. However func3 is an impure function. This means it cannot directly call loadEnv, an impure function. This means the environment has to be passed through as a variable to the other functions, just so they can ultimately pass it to func3. In a language with global variables, we could save env as a global value in main. Then func3 could access it directly. There would be no need to have it as a parameter to func1 and func2. In larger programs, these “pass-through” variables can cause a lot of headaches.

The Reader Solution

The Reader monad solves this problem. It effectively creates a global read-only value of a specified type. All functions within the monad can “read” the type. Let’s look at how the Reader monad changes the shape of our code. Our functions no longer need the Environment as an explicit parameter, as they can access it through the monad.

main :: IO ()
main = do
  env <- loadEnv
  let str = runReader func1 env
  print str

data Environment = Environment
  { param1 :: String
  , param2 :: String
  , param3 :: String }

loadEnv :: IO Environment
loadEnv = …

func1 :: Reader Environment String
func1 = do
  res <- func2
  return (“Result: “ ++ (show res))

func2 :: Reader Environment Int
func2 = do
  env <- ask
  let res3 = func3 env
  return (2 + (floor res3))

func3 :: Environment -> Float
...

The ask function unwraps the environment so we can use it. The monad’s bind action allows us to glue different Reader actions together together. In order to call a reader action from pure code, all we need to do is call the runReader function and supply the environment as a parameter. All functions within the action will be able to treat it like a global variable.

It might not seem like we’ve accomplished much, but our code is much more intuitive now. We keep func3 as it was. It makes sense to describe it as a function from an Environment to a value. However, our other two functions no longer take the environment as an explicit parameter. They simply exist in a context where the environment is a global variable.

Accumulating Values

Now, to motivate the Writer monad, let’s talk about the accumulation problem. Suppose we have a few different functions. Each will perform some string operations we’ve assigned an arbitrary “cost” to. We want to keep track of how “expensive” it was to run the full computation. We can do this by using accumulator arguments to keep track of the cost we’ve seen so far. We then keep passing the accumulated value along.

-- Calls func2 if even length, func3 and func4 if odd
func1 :: String -> (Int, String)
func1 input = if length input `mod` 2 == 0
  then func2 (0, input)
  else (i1 + i2, str1 ++ str2)
    where
      (i1, str1) = func3 (0, tail input)
      (i2, str2) = func4 (0, take 1 input)

-- Calls func4 on truncated version
func2 :: (Int, String) -> (Int, String)
func2 (prev, input) = if (length input) > 10
  then func4 (prev + 1, take 9 input)
  else (10, input)

-- Calls func2 on expanded version if a multiple of 3
func3 :: (Int, String) -> (Int, String)
func3 (prev, input) = if (length input) `mod` 3 == 0
  then (prev + f2resI + 3, f2resStr)
  else (prev + 1, tail input)
  where
    (f2resI, f2resStr) = func2 (prev, input ++ "ab")

func4 :: (Int, String) -> (Int, String)
func4 (prev, input) = if (length input) < 10
  then (prev + length input, input ++ input)
  else (prev + 5, take 5 input)

However, an Int isn’t the only type of value we could accumulate. We could instead be accumulating a list of strings to print as log messages so we know what computations were run. There is a generalization of this behavior: the Monoid typeclass.

The Monoid Typeclass

In this example, Int is a simple example of a Monoid. Let’s look at the monoid typeclass definition:

class Monoid a where
  mempty :: a
  mappend :: a -> a -> a

This is effectively an accumulation class. It defines two functions. The mempty function is an initial value for our monoid. Then with mappend, we can combine two values of this type into a result. It is quite easy to how we can make a monoid instance for Int:

instance Monoid Int where
  memty = 0
  mappend a b = a + b

Our accumulator starts at 0, and we combine values by adding them.

Using Writer to Track the Accumulator

The Writer monad is parameterized by some monoidal type. Its main job is to keep track of an accumulated value of this type. So it’s operations live in the context of having a global value that they can modify in this particular way. We can change our code examples above to use the Writer monad as follows:

func1 :: String -> (String, Int)
func1 input = if length input `mod` 2 == 0
  then runWriter (func2 input)
  else runWriter $ do
    str1 <- func3 input
    str2 <- func4 (take 1 input)
    return (str1 ++ str2)

func2 :: String -> Writer Int String
func2 input = if (length input) > 10
  then do
    tell 1
    func4 (take 9 input)
  else do
    tell 10
    return input

func3 :: String -> Writer Int String
func3 input = if (length input) `mod` 3 == 0
  then do
    tell 3
    func2 (input ++ “ab”)
  else do
    tell 1
    return $ tail input

func4 :: String -> Writer Int String
func4 input = if (length input) < 10
  then do
    tell (length input)
    return (input ++ input)
  else do
    tell 5
    return (take 5 input)

Notice we no longer need to actually explicitly keep track of the accumulator. It is now wrapped by the Writer monad. We can increase it in any of our functions by calling “tell”. Now our code is much simpler and our types are cleaner.

Conclusion

The Reader and Writer monads both offer pure functional ways to deal with common side effects. The Reader monad allows you to keep track of a shared global state. It allows you to avoid passing that state as an explicit parameter to functions that don’t really use it. The Writer monad allows you to keep track of a global accumulated value using a monoid. Next week we’ll learn how we can wrap these ideas into one with the State monad!

Hopefully this article has helped convinced you that monads (and Haskell for that matter) aren’t all that scary! If this has inspired you to pick up Haskell and start writing some code, check out our free checklist for getting stated!

Not quite ready for monads but want to try some different Haskell skills? Check out our recursion workbook. It includes 2 chapters of material on recursion and higher order functions, as well as 10 practice problems with a test harness.

Read More
James Bowen James Bowen

(Finally) Understanding Monads (Part 1)

We should now have a decent grasp on functors and applicative functors (check out the links if you aren’t!). Now it’s time to take the next step up. We’re going to tackle the dreaded concept of Monads. There are dozens of monad tutorials and descriptions on the internet. This makes sense. Monads are vital to writing any kind of meaningful program in Haskell. They aren’t the hardest concept in functional programming, but they are the biggest roadblock because of their importance. In this series of articles, we’re going to try tackling the concept in small, manageable chunks.

So without further ado, here’s my crack at a definition: A Monad wraps a value or a computation with a particular context. A monad must define both a means of wrapping normal values in the context, and a way of combining computations within the context.

This definition is quite broad. So let’s look at a more practical level to try to make sense of this.

The Monad Typeclass

Just like with functors and applicative functors, Haskell represents monads with a type class. It has two functions:

class Monad m where
  return :: a -> m a
  (>>=) :: m a -> a -> m b -> m b

These two functions correspond to the two ideas from above. The return function specifies a how to wrap values in the monad’s context. The >>= operator, which we call the “bind” function, specifies how to combine two operations within the context. Let’s clarify this further by exploring a few specific monad instances.

The Maybe Monad

Just as Maybe is a functor and an applicative functor, it is also a monad. To motivate the Maybe monad, let’s consider this code.

maybeFunc1 :: String -> Maybe Int
maybeFunc1 “” = Nothing
maybeFunc1 str = Just $ length str

maybeFunc2 :: Int -> Maybe Float
maybeFunc2 i = if i `mod` 2 == 0
  then Nothing
  Else Just ((fromIntegral i) * 3.14159)

maybeFunc3 :: Float -> Maybe [Int]
maybeFunc3 f = if f > 15.0
  then Nothing
  else $ Just [floor f, ceil f]

runMaybeFuncs :: String -> Maybe [Int]
runMaybeFuncs input = case maybeFunc1 input of
  Nothing -> Nothing
  Just i -> case maybeFunc2 i of
    Nothing -> Nothing
    Just f -> maybeFunc3 f

We can see we’re starting to develop a hideous triangle pattern as we continue pattern matching on the results of successive function calls. If we were to add more Maybe functions onto this, it would keep getting worse. When we consider Maybe as a monad, we can make the code much cleaner. Let’s take a look at how Haskell implements Maybe as a monad to see how.

instance Monad Maybe where
  return = Just
  Nothing >>= _ = Nothing
  Just a >>= f = f a

The context the Maybe monad describes is simple. Computations in Maybe can either fail or succeed with a value. We can take any value and wrap it in this context by calling the value a “success”. We do this with the Just constructor. We represent failure by Nothing.

We combine computations in this context by examining the result of the first computation. If it succeeded, we takes its value, and pass it to the second computation. If it failed, then we have no value to pass to the next step. So the total computation is a failure. Let’s look at how we can use the bind operator to combine our operations:

runMaybeFuncs :: String -> Maybe [Int]
runMaybeFuncs input = maybeFunc1 input >>= maybeFunc2 >>= maybeFunc3

This looks much cleaner! Let’s see why the types work out. The result of maybeFunc1 input is simply Maybe Int. Then the bind operator allows us to take this Maybe Int value and combine it with maybeFunc2, whose type is Int -> Maybe Float. The bind operator resolves these to a Maybe Float. Then we pass this similarly through the bind operator to maybeFunc3, resulting in our final type, Maybe [Int].

Your functions will not always combine so cleanly though. This is where do notation comes into play. We can rewrite the above as:

runMaybeFuncs :: String -> Maybe [Int]
runMaybeFuncs input = do
  i <- maybeFunc1 input
  f <- maybeFunc2 f
  maybeFunc3 f

The <- operator is special. It effectively unwraps the value on the right-hand side from the monad. This means the value i has type Int, even though the result of maybeFunc1 is Maybe Int. The bind operation happens under the hood, and if the function returns Nothing, then the entire runMaybeFuncs function will return Nothing.

At first glance, this looks more complicated than the bind example. However, it gives us a lot more flexibility. Consider if we wanted to add 2 to the integer before calling maybeFunc2. This is easy to deal with in do notation, but more difficult when simply using binds:

runMaybeFuncs :: String -> Maybe [Int]
runMaybeFuncs input = do
  i <- maybeFunc1 input
  f <- maybeFunc2 (i + 2)
  maybeFunc3 f

-- Not so nice
runMaybeFuncsBind :: String -> Maybe [Int]
runMaybeFuncsBind input = maybeFunc1 input
  >>= (\i -> maybeFunc2 (i + 2))
  >>= maybeFunc3

The gains are even more obvious if we want to use multiple previous results in a function call. Using binds, we would have to continually accumulate arguments in anonymous functions. One note about do notation: we never use <- to unwrap the final operation in a do-block. Our call to maybeFunc3 has the type Maybe [Int]. This is our final type (not [Int]) so we do not unwrap.

The Either Monad

Now, let’s examine the Either monad, which is quite similar to the Maybe monad. Here’s the definition:

instance Monad (Either a) where
  return r = Right r
  (Left l) >>= _ = Left l
  (Right r) >>= f = f r

Whereas the Maybe either succeeds with a value or fails, the Either monad attaches information to failures. Just like Maybe, it wraps values in its context by calling them successful. The monadic behavior also combines operations by short-circuiting on the first failure. Let’s see how we can use this to make our code from above more clear.

maybeFunc1 :: String -> Either String Int
maybeFunc1 “” = Left “String cannot be empty!”
maybeFunc1 str = Right $ length str

maybeFunc2 :: Int -> Either String Float
maybeFunc2 i = if i `mod` 2 == 0
  then Left “Length cannot be even!”
  else Right ((fromIntegral i) * 3.14159)

maybeFunc3 :: Float -> Either String [Int]
maybeFunc3 f = if f > 15.0
  then Left “Float is too large!”
  else $ Right [floor f, ceil f]

runMaybeFuncs :: String -> Either String [Int]
runMaybeFuncs input = do
  i <- maybeFunc1 input
  f <- maybeFunc2 i
  maybeFunc3 f

Before, every failure just gave us a Nothing value:

>> runMaybeFuncs ""
Nothing
>> runMaybeFuncs "Hi"
Nothing
>> runMaybeFuncs "Hithere"
Nothing
>> runMaybeFuncs "Hit"
Just [9,10]

Now when we run our code, we can look at the resulting error string, and this will tell us which function actually failed.

>> runMaybeFuncs ""
Left "String cannot be empty!"
>> runMaybeFuncs "Hi"
Left "Length cannot be even!"
>> runMaybeFuncs "Hithere"
Left "Float is too large!"
>> runMaybeFuncs "Hit"
Right [9,10]

Notice we parameterize the Either monad by the error type. If we have:

maybeFunc2 :: Either CustomError Float
…

This function is in a different monad now. It won’t be quite as simple to combine this with our other functions. If you’re curious how we might do this, check out this answer on quora.

The IO Monad

The IO Monad is perhaps the most important monad in Haskell. It is also one of the hardest monads to understand starting out. Its actual implementation is a bit too intricate to discuss when first learning monads. So we’ll learn by example.

The IO monad wraps computations in the following context: “This computation can read information from or write information to the terminal, file system, operating system, and/or network”. If you want to get user input, print a message to the user, read information from a file, or make a network call, you’ll need to do so within the IO Monad. These are “side effects”. We cannot perform them from “pure” Haskell code.

The most important job of pretty much any computer program is to interact with the outside world in some way. For this reason, the root of all executable Haskell code is a function called main, with the type IO (). So every program starts in the IO monad. From here you can get any input you need, call into relatively “pure” code with the inputs, and then output the result in some way. The reverse does not work. You cannot call into IO code from pure code, the same way you can call into a Maybe function from pure code.

Let’s look at a simple program showing a few of the basic IO functions. We’ll use do-notation to illustrate the similarity to the other monads we’ve discussed. We list the types of each IO function for clarity.

main :: IO ()
main = do
  -- getLine :: IO String
  input <- getLIne
  let uppercased = map Data.Char.toUpper input
  -- print :: String -> IO ()
  print uppercased

So we see once again each line of our program has type IO a. (A let statement can occur in any monad). Just as we could unwrap i in the maybe example to get an Int instead of a Maybe Int, we can use <- to unwrap the result of getLine as a String. We can then manipulate this value using string functions, and pass the result to the print function.

This is a simple echo program. It reads a line from the terminal, and then prints the line back out in all caps. Hopefully it gives you a basic understanding of how IO works. We’ll get into more details in the next couple articles.

Summary

A monad wraps computations in a particular context. It defines functions for wrapping values in its context, and combining operations in the context. Maybe is a monad. We describe its context by saying its computations can succeed or fail. Either is similar to Maybe, except it can add error information to failures. The IO monad is hugely important, encapsulating the context of operations reading from and writing to the terminal, network, and file system. The easiest way to learn monadic code is to use do notation. In this notation, every line has a right-side value of the monad. You can then unwrap the value on the left side using the <- operator.

Stay tuned next week as we continue our exploration of monads. We’ll examine the Reader and Writer monads, and demonstrate how they encapsulate different kinds of side effects then we might get from the IO monad.

Hopefully this article has started you off on (finally) understanding monads. If you haven’t written any Haskell code yet and want to get started so you can test your knowledge of monads, be sure to check out our free checklist for getting started with Haskell!

Not quite ready for monads but want to try some different Haskell skills? Check out our recursion workbook. It includes 2 chapters of material on recursion and higher order functions, as well as 10 practice problems with a test harness.

Read More
James Bowen James Bowen

Applicatives: One Step Further

So last week, we discussed the Functor typeclass. We found it allows us to run transformations on data regardless of how the data is wrapped. No matter if our data were in a List, a Maybe, an Either, or even a custom type, we could simply call fmap. However, what happens when we try to combine wrapped data? For instance, if we try to have GHCI interpret these calculations, we’ll get type errors:

>> (Just 4) * (Just 5)
>> Nothing * (Just 2)

Functors Falling Short

Can functors help us here? We can use fmap to wrap multiplication by the particular wrapped Maybe value:

>> let f = (*) <$> (Just 4)
>> :t f
f :: Num a => Maybe (a -> a)
>> (*) <$> Nothing
Nothing

This gives us a partial function wrapped in a Maybe. But we still cannot unwrap this and apply it to (Just 5) in a generic fashion. So we have to resort to code specific to the Maybe type:

funcMaybe :: Maybe (a -> b) -> Maybe a -> Maybe b
funcMaybe Nothing _ = Nothing
funcMaybe (Just f) val = f <$> val

This obviously won’t work with other functors types.

Applicatives to the Rescue

This is exactly what the Applicative typeclass is for. It has two main functions:

pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b

The pure function takes some value and wraps it in a minimal context. The <*> function, called sequential application, takes two parameters. First, it takes a function wrapped in the context. Next, a wrapped value. Its output is the result of applying the function to the value, rewrapped in the context. An instance is called an applicative functor because it allows us to apply a wrapped function. Since sequential application takes a wrapped function, we typically begin our use of applicatives by wrapping something with either pure or fmap. This will become more clear with some examples.

Let’s first consider multiplying Maybe values. If we are multiply by a constant value we can use the functor approach. But we can also use the applicative approach by wrapping the constant function in pure and then using sequential application:

>> (4 *) <$> (Just 5)
Just 20
>> (4 *) <$> Nothing
Nothing
>> pure (4 *) <*> (Just 5)
Just 20
>> pure (4 *) <*> Nothing
Nothing

Now if we want to multiply 2 maybe values, we start by wrapping the bare multiplication function in pure. Then we sequentially apply both Maybe values:

>> pure (*) <*> (Just 4) <*> (Just 5)
Just 20
>> pure (*) <*> Nothing <*> (Just 5)
Nothing
>> pure (*) <*> (Just 4) <*> Nothing
Nothing

Implementing Applicatives

From these examples, we can tell the Applicative instance for Maybe is implemented exactly how we would expect. The pure function simply wraps a value with Just. Then to chain things together, if either the function or the value is Nothing, we output Nothing. Otherwise apply the function to the value and re-wrap with Just.

instance Applicative Maybe where
  pure = Just
  (<*>) Nothing _ = Nothing
  (<*>) _ Nothing = Nothing
  (<*>) (Just f) (Just x) = Just (f x)

The Applicative instance for Lists is a little more interesting. It doesn’t exactly give the behavior we might first expect.

instance Applicative [] where
  pure a = [a]
  fs <*> xs = [f x | f <- fs, x <- xs]

The pure function is what we expect. We take a value and wrap it as a singleton in a list. When we chain operations, we now take a LIST of functions. We might expect to apply each function to the value in the corresponding position. However, what actually happens is we apply every function in the first list to every value in the second list. When we have only one function, this results in familiar mapping behavior. But when we have multiple functions, we see the difference:

>> pure (4 *) <*> [1,2,3]
[4,8,12]
>> [(1+), (5*), (10*)] <*> [1,2,3]
[2,3,4,5,10,15,10,20,30]

This makes it easy to do certain operations, like finding every pairwise product of two lists:

>> pure (*) <*> [1,2,3] <*> [10,20,30]
[10,20,30,20,40,60,30,60,90]

You might be wondering how we might do parallel application of functions. For instance, we might want to use the second list example above, but have the result be [2,10,30]. There is a construct for this, called ZipList! It is a newtype around list, whose Applicative instance is designed to use this behavior.

>> ZipList [(1+), (5*), (10*)] <*> [5,10,15]
ZipList {getZipList = [6,50,150]}

Summary

  1. Applicative functors take the idea of normal functors one step further.
  2. They allow function application to occur within the wrapper context.
  3. In some circumstances, this allows us to reuse generic code.
  4. In other cases, this gives us clean idioms to express simple concepts and solve common problems.
Read More
James Bowen James Bowen

The Easiest Haskell Idiom

Once you master the basics of Haskell, the next important step is to understand the patterns that make it easier to write good, idiomatic Haskell code. The next few posts will focus on some of the most important patterns to learn. The simplest of these is functors.

A Simple Example

Here’s a simple example to start us on our way. This code converts an input string like “John Doe 24” into a tuple. We want to consider all inputs though, so the resulting type is a Maybe.

tupleFromInputString :: String -> Maybe (String, String, Int)
tupleFromInputString input = if length stringComponents /= 3
  then Nothing
  else Just (stringComponents !! 0, stringComponents !! 1, age)
  where 
    stringComponents = words input
    age = (read (stringComponents !! 2) :: Int)

This simple function simply takes a string and converts it into parameters for first name, last name, and age. Suppose we have another part of our program using a data type to represent a person instead of a tuple. We might write a conversion function between these two different types. We want to account for the possibility of failure. So we’ll have another function handling that case.

data Person = Person {
  firstName :: String,
  lastName :: String,
  age :: Int
}

personFromTuple :: (String, String, Int) -> Person
personFromTuple (fName, lName, age) = Person fName lName age

convertTuple :: Maybe (String, String, Int) -> Maybe Person
convertTuple Nothing = Nothing
convertTuple (Just t) = Just (personFromTuple t)

A Change of Format

But imagine our original program changes to read in a whole list of names:

listFromInputString :: String -> [(String, String, Int)]
listFromInputString contents = mapMaybe tupleFromInputString (lines contents)

tupleFromInputString :: String -> Maybe (String, String, Int)
...

Now if we passed this result to the code using Person, we would have to change the type of the convertTuple function. It would have a parallel structure though. Maybe and List can both act as containers for other values. Sometimes, we don’t care how values are wrapped. We just want to transform whatever underlying value exists, and then return the new value in the same wrapper.

Introduction to Functors

With this idea in mind, we can start understanding functors. First and foremost, Functor is a typeclass in Haskell. In order for a data type to be an instance of the Functor typeclass, it must implement a single function: fmap.

fmap :: (a -> b) -> f a -> f b

The fmap function takes two inputs. First, it demands a function between two data types. The second parameter is some container of the first type. The output then is a container of the second type. Now let’s look at a few different Functor instances for some familiar types. For lists, fmap is simply defined as the basic map function:

instance Functor [] where
  fmap = map

In fact, fmap is a generalization of mapping. For example, the Map data type is also a functor. It uses its own map function for fmap. Functors simply take this idea of transforming all underlying values and apply it to other types. With this in mind, let’s observe how Maybe is a functor:

instance Functor Maybe where
  fmap _ Nothing = Nothing
  fmap f (Just a) = Just (f a)

This looks a lot like our original convertTuple function! If we have no value in the first place, then the result is Nothing. If we do have a value, simply apply the function to the value, and rewrap it in Just. The Either data type can be seen as a Maybe type with more information about how it failed. It has similar behavior:

instance Functor (Either a) where
    fmap _ (Left x) = Left x
    fmap f (Right y) = Right (f y)

Note the first type parameter of this instance is fixed. Only the second parameter of an Either value is changed by fmap. Based on these examples, we can see how to rewrite convertTuple to be more generic:

convertTuple :: Functor f => f (String, String, Int) -> f Person
convertTuple = fmap personFromTuple

Making Our Own Functors

We can also take our own data type and define an instance of Functor. Suppose we have the following data type representing a directory of local government officials. It is parameterized by the type a. This means we allow different directories using different representations of a person:

data GovDirectory a = GovDirectory {
  mayor :: a,
  interimMayor :: Maybe a,
  cabinet :: Map String a,
  councilMembers :: [a]
}

One part of our application might represent people with tuples. Its type would be GovDirectory (String, String, Int). However, another part could use the type GovDirectory Person. We can define the following Functor instance for GovDirectory by defining fmap. Since our underlying types are mostly functors themselves, this mostly involves calling fmap on the individual fields!

instance Functor GovDirectory where
  fmap f oldDirectory = GovDirectory {
    mayor = f (mayor oldDirectory),
    interimMayor = f <$> interimMayor oldDirectory,
    cabinet = f <$> cabinet oldDirectory,
    councilMembers = f <$> councilMembers oldDirectory
  }

Note <$> is simply a synonym for fmap. Now we have our own functor instance, sp transforming the underlying data type of our directory class is easy! We can just use:

convertTuple <$> oldDirectory

Summary

  1. Functors, in general, wrap some kind of data
  2. In Haskell, Functor is a typeclass, with a single function fmap
  3. The fmap function allows us to transform the underlying data without caring how the data is contained.
  4. This allows us to write much more flexible code in certain circumstances.

Stay tuned for next week, when we’ll discuss applicative functors! If you’re starting to get a grasp for Haskell and want to try new skills, be sure to check out our free workbook on Recursion, which comes with 10 practice problems!

Read More
James Bowen James Bowen

Easing Haskell's Intimidating Glare

All of my previous articles have discussed basic language features and concepts in Haskell. My hope has been to provide new Haskellers with suitable starting material to help them get started. Even just as a language, Haskell is complex. There are many technical challenges and paradigm shifts one has to overcome to learn it. This is especially true for those coming from imperative languages.

However, this article will focus on something equally important. It’s no secret that most people consider Haskell not just a difficult language to learn technically, but an intimidating language. It has undoubted psychological hurdles. People seem to give up on Haskell at a higher rate than most other languages. By naming these problems, we can overcome them.

An Academic Language

People have long considered Haskell primarily as a research language. It builds on the lambda calculus, possibly the simplest, purest programming language. This gives it a host of connections to cool concepts in abstract mathematics, primarily the province of professors and PhD. students. The connection is so elegant many mathematical ideas can be well represented in Haskell.

But this connection has a price in accessibility. Important Haskell concepts include functors, monads, categories, etc. These are cool, but few without a math degree have any intuition for what the terms mean. Compare these to terms from other languages: class, iterator, loop, template. These terms are a lot more intuitive, so the languages using them have an automatic advantage in accessibility.

Aside from this terminology point though, the great academic interest is a good thing, not a bad thing. However, on the production side of things, the tooling has not been sufficient. It was simply too difficult to maintain a large-scale Haskell project. As a result, companies had little incentive to use it. This meant there was little to no balance of the academic influence.

Knowledge Distribution

The net result of Haskell’s academic primacy is a skewed knowledge base. The nature of academia is relatively few people spend a large amount of time on a relatively small set of problems. Consider another academic field, like virology. You have some experts who understand viruses at an extremely high level, and the rest of us know quite little. There are no hobbyist virologists. Unfortunately, this kind of distribution is not conducive to introducing new people to a topic.

Naturally, people have to learn from those who know more than they do. But the truth is they don’t want their teachers to be too much better. It helps tremendously to learn from someone who was in your shoes not too long ago. They’ll more likely remember the pitfalls and frustrations they encountered early on, so they’ll be able to help you avoid them. But when the distribution skews towards the extreme, there is no middle class. There are fewer people who can optimally teach new learners. In addition to not remembering old mistakes, experts tend to use overly complicated terminology. New folks may feel intimidated by this and despair.

Turning the Tide of Production

The lack of production work mentioned above contributes substantially to this divide. Many other languages, like C++, have strong academic followings. But since so many companies use C++ in production, it does not face the knowledge distribution problem Haskell does. Companies using C++ have no choice but to train people to use the language. Many of these people stick with the language long enough to train the next generation. This creates a more normal looking knowledge distribution curve.

The good news for Haskell is there have been major tooling improvements in the last few years. This has brought about a renaissance for the language. More companies are starting to use it in production. More meetups are happening; more people are writing libraries for the most crucial tasks. If this trend continues, Haskell will hopefully reach a tipping point, normalizing the knowledge distribution curve.

The Key Insight

If you are someone who is interested in learning Haskell, or who has tried learning Haskell in the past, there is one key thing to know. While the abstract mathematics is cool, understanding it is mostly unnecessary. Monads are essential, there is no denying. But category theory is overkill for most day-to-day problems you’ll solve. The dozens of language extensions might seem intimidating, but you can pick them up one-by-one as you go.

At the last Haskell eXchange, Don Stewart from Standard Chartered gave a talk about the company’s use of Haskell. He explained they rarely use anything outside of vanilla Haskell constructs*. They just don’t need to. Anything you can do with, say, lenses, you can accomplish without them.

Haskell is different from most other programming languages. It constrains you in ways those languages do not. But the constraints are not nearly as binding as they seem. You can’t use for loops. So use recursion. You can’t re-assign variables. So create new names for expressions. You just have take it one step at a time.

Taking Action

If this has inspired you to get started with Haskell, check out this checklist to learn the tools you need to get started.

If you’re already familiar with the basics and want to take the next step up, you should take a look at our workbook. You’ll learn about recursion and get to take a shot at 10 practice problems to test your skills!

Note

*At least with respect to their normal Haskell code. Some of their code is in a proprietary language of theirs called Mu, which is built on Haskell but obviously different.

Read More
James Bowen James Bowen

Faster Code with Laziness

Most programming languages use an evaluation paradigm called eager evaluation. This means the moment the program execution reaches an expression, the expression is evaluated. This makes sense in imperative languages. An imperative program is a series of commands. To execute the commands in order, it is best to evaluate each expression immediately.

Haskell is different. As we’ve discussed, a Haskell program is actually a series of expressions to be evaluated. However, Haskell uses lazy evaluation. It only evaluates the expressions it absolutely needs to in order to produce the program’s output. Furthermore, it only evaluates an expression at the moment it is needed, rather than the moment it is encountered.

Laziness Under the Hood

So how does Haskell accomplish this? To understand, we have to take a deeper look at Haskell’s memory model. Consider this simple program:

main = do
  let myTuple = (“first”, map (*2) [1,2,3,4])
  print “Hello”
  print $ fst myTuple
  print head snd myTuple
  print (length (snd myTuple))
  print $ snd myTuple

When the first print statement (“Hello”) happens, the expression myTuple is actually completely unevaluated, even though it is defined before the print statement. It is represented in memory by what is called a “thunk”. Of course, our program knows how to evaluate this thunk when the time comes. But for now, no value is there. When it prints the first element of the tuple, it still doesn’t completely evaluate myTuple. When the compiler sees us call the fst function on myTuple, it knows myTuple must be a tuple. Instead of seeing a single thunk at this point, the compiler sees myTuple as a tuple containing two unevaluated thunks.

Next, we print the first element of myTuple. Printing an expression forces it to be completely evaluated. So after this, the compiler sees myTuple as a tuple containing a string in its first position and an unevaluated thunk in its second position.

At the next step, we print the head of the second element of myTuple. This tells the compiler this second element must be a non-empty list. If it were empty, our program would actually crash here. This forces the evaluation of this first element (2). However, the remainder of the list remains an unevaluated thunk.

Next, we print the length of the list. Haskell will do enough work here to determine how many elements are in the list, but it will not actually evaluate any more items. The list is now an evaluated first element, and 3 unevaluated thunks. Finally, we print the full list. This evaluates the list in its entirety. If we did not do this last step, the final 3 elements would never be evaluated.

Advantages of Laziness

So all of this seems extremely convoluted. How exactly does laziness help us? Well, the main benefit is it makes our program faster. Consider the following example:

function1 :: Int
function1 = function2 exp1 exp2 exp3
  where
    exp1 = reallyLongFunction 1234
    exp2 = reallyLongFunction 3151
    exp3 = reallyLongFunction 8571

function2 :: Int -> Int -> Int -> Int
function2 exp1 exp2 exp3 = if exp1 < 1000
  then exp2
  else if exp1 < 2000
    then exp3
    else exp1

Comparable code in C++ or Java would need to make all three expensive calls to reallyLongFunction before calling function2 with the results. But in Haskell, the program will not call reallyLongFunction until it absolutely needs to.

So in this example, we will always evaluate exp1 in order to determine the result of the if statement in function2. However, if we happen to have exp1 >= 2000, then we’ll never evaluate exp2 or exp3! We don’t need them! We’ll save ourselves from having to make the expensive calls to reallyLongFunction. As a result, our program will run faster.

For another example, suppose we have a quicksort function. The quicksort algorithm works like this:

  1. Pick a partition element
  2. Move all elements smaller than the partition into one list
  3. Move all elements larger than the partition into another list
  4. Sort these two smaller lists recursively.
  5. Combine them with the partition.

In Haskell, we can leverage this function quite easily to just get the smallest 3 elements of a list:

smallest3 :: [Int] -> [Int]
smallest3 input= take 3 (quicksort input)

quicksort :: Ord a => [a] -> [a]
...

Since Haskell is lazy, we’ll never have to touch the larger half of the partition, even in the recursive calls. In an eager language, the compiler would run the full sorting algorithm. This would take much longer than we need.

Another cool advantage of laziness is the possibility of infinite lists. Here are two ways to create infinite lists:

allTwos :: [Int]
allTwos = repeat 2

first4 :: [Int]
first4 = cycle [1,2,3,4]

Infinite lists can provide elegant solutions to many list problems. We can never do something that will bring the full infinite list into scope by, say, printing it. But we can take the first n elements of an infinite list! The "infinite" remainder will stay as an unevaluated thunk.

Disadvantages of Laziness

Laziness does have some disadvantages though. In particular, when using recursion or recursive data structures, unevaluated thunks can build up in heap memory. If they consume all your memory, your program will crash. In particular, the foldl function suffers from this problem. The following usage will probably fail due to a memory leak.

foldl (+) 0 [1..10^7]

Laziness can also make it harder to reason about our code. Just because a number of expressions are defined in the same where clause does not mean that they will be evaluated at the same time. This can easily confuse beginners.

Introducing Strictness

Now, if we want, there are a few ways to "remove" laziness from our code and instead introduce strictness. The first is the seq function. It has the type:

seq :: a -> b -> b

It takes two arguments. The output is simply the second argument. However, the function is "strict" in its first argument. The first argument will be fully evaluated no matter what. For example, consider the following:

>> seq 3 "Hello"
"Hello"
>> seq undefined 3
*** Exception: Prelude.undefined
CallStack (from HasCallStack):
  error, called at libraries/base/GHC/Err.hs:79:14 in base:GHC.Err
  undefined, called at <interactive>:2:5 in interactive:Ghci2

The foldl function has a strict counterpart foldl’. Observe how it uses seq to avoid the memory leak problem:

foldl’ f z [] = z
foldl’ f z (x:xs) = let z’ = z `f` x
  in seq z’ $ foldl’ f z’ xs

Try the same addition example from above with foldl’. It works perfectly well. By forcing the evaluation of the sum, we prevent the buildup of unevaluated thunks on the heap. This avoids the memory leak.

We can also introduce strictness into our datatypes. Let’s compare two different Person classes.

data Person = Person
  { pFirstName :: String
  , pLastName :: String
  , pAge :: Int }

data StrictPerson  = StrictPerson
  { firstName :: !String
  , lastName :: !String
  , age :: !Int }

The StrictPerson data type (with the exclamation points) is strict in all its arguments. Whenever a StrictPerson object is evaluated, all its fields will be evaluated as well. This is not the case with the regular Person type. One difference is that we can use record syntax to create an incomplete person. We cannot create an incomplete StrictPerson:

-- This works! (though the compiler might give us a warning)
ageOfIncomplete :: Int
ageOfIncomplete = pAge incompletePerson
  where
    incompletePerson = Person { pAge = 25}

-- This will not compile
ageOfIncompleteStrict :: Int
ageOfIncompleteStrict = age incompletePerson
  where 
    incompletePerson = StrictPerson {age = 25}

Naturally, even the Person object would fail if we tried to access the undefined pFirstName field. In general, it is good to introduce strictness into your data types. The exception is if one of the fields is possibly unnecessary and expensive to compute.

Summary

  1. Haskell is a lazy language. It does not evaluate expressions until it absolutely must.
  2. This frequently allows our programs to save time by avoiding unnecessary computation, but they are at more of a risk to leak memory.
  3. There are ways of introducing strictness into our programs when we don’t want lazy evaluation.

If you're ready to try out your Haskell skills, check out our free workbook featuring content on recursion and higher order functions, plus 10 practice problems!

Read More
James Bowen James Bowen

Immutability is Awesome

Immutability is a confusing concept in Haskell. It is the property of all Haskell expressions that, once they are assigned to a name, they CANNOT change their value. To most programmers, this seems like a completely crazy idea. How can you program at all when you cannot change the value of an expression? To start, let’s consider this example of how mutability works in Python:

>> a = [1,2,3]
>> a.reverse()
>> a
[3, 2, 1]
>> b = a.reverse()
>> a
[1, 2, 3]
>> b
(No output)
>>

We see now when we call the reverse function on a, the value of a actually changes. We call a later, and we get a reversed list. Meanwhile, reverse has no return value. When we assign the result of a.reverse() to b, we get nothing.

Let’s see what happens in Haskell:

>> let a = [1,2,3]
>> reverse a
[3,2,1]
>> a
[1,2,3]

This time, the value of a did not change. Calling reverse seems to have produced the output we expected, but it had no impact on the original variable. To access the reversed list, we would need to assign it to a name (by using let).

Build it Again

To understand what it is happening, let’s first look at the type signature for reverse:

reverse :: [a] -> [a]

We see reverse takes one argument, a list. It then outputs a list of the same type. It’s a pure function, so it cannot change the input list! Instead, it constructs a completely new list with the same elements as the input, only in reverse order. This may seem strange, but it has huge implications on our ability to reason about code. Consider the following:

operateOnList :: [Int] -> [Int]
operateOnList ints = concat [l1, l2, l3]
  where 
    l1 = funcOnList1 ints
    l2 = funcOnList2 ints
    l3 = funcOnList3 ints

Comparable python code is much harder to reason about. Each function might mutate the input list, so we don’t know what value is passed to the second and third functions. However, in Haskell, we know we will pass the exact same list as a parameter to each of these functions! This simple principle makes it extremely easy to refactor Haskell code.

Efficiency Concerns

You might be wondering, isn’t immutability really inefficient? If reverse creates a completely new list, is it copying every element from the old list? In fact, it does not copy the elements. It simply uses the exact same elements from the original list. In C++ or Python, this would raise the question of what would happen when we modify one of the elements in the first list. Wouldn’t the reversed list also change? But in Haskell, we can’t modify the items in the first list because of immutability! So we’re safe!

Now, remaking the data structure itself does introduce some overhead. In the case of reverse we do have to create a new linked list with a new set of pointers. But in most cases, it’s not so bad. Consider the concatenation operator.

(:) :: a -> [a] -> [a]

This function takes an element and a list and returns a new list with the new element appended to the front of the old list. This is quite efficient, because the new list simply uses the old list as its tail. There is no need to re-create a separate data structure. Immutability protects us from all the nasty side effects of having two data structures operate on the same data. Other data structures work in the same way. For instance, the insert function of the Map data structure takes a key, a value, and an old map, and returns a new map with all the elements of the old map, plus the new mapping between the key and the value.

insert :: k -> v -> Map k v -> Map k v

This is done efficiently, without having to recreate any of the structure of the old map or any of its underlying elements. In a previous article we discussed record syntax on algebraic data types. We saw how we could “modify” objects with record syntax:

makeJohnDoe :: Person -> Person
makeJohnDoe person = person
  { firstName = “John”
  , lastName = “Doe” }

But this does not change the old Person object. This has the same effect as reversing a list. It recreates the Person structure, but none of the underlying elements. And the result is a completely new Person object. So remember:

Summary

  1. Expressions in Haskell are immutable. They cannot change after they are evaluated.
  2. Immutability makes refactoring super easy and code much easier to reason about.
  3. To “change” an object, most data structures provide methods taking the old object and creating a new copy.

If you want to get started with Haskell, be sure to check out our checklist to make sure you have everything you need!

Read More
James Bowen James Bowen

Many Types, One Behavior

In the last two articles, we learned all the basic ways to make our own data types. Now we need to ask a question: what happens when we have multiple types with similar behavior? Polymorphic code can work with many different types. This flexibility makes it easier to work with and better.

Equality

For a concrete example, let’s consider the simple issue of comparing two objects for equality. Let’s review our person data type:

data Person = Person
  { firstName :: String
  , lastName :: String
  , age :: Int
  , hometown :: String
  , homestate :: String }

We’ve seen before how we can compare integers and strings using the equality function (==). What happens when we try to use this with two Person objects?

>> 1 == 1
True
>> “Hi” == “Bye”
False
>> let p1 = Person “John” “Doe” 23 “Kansas City” “Missouri”
>> let p2 = Person “Jane” “Doe” 22 “Columbus” “Ohio”
>> p1 == p2
    No instance for (Eq Person) arising from a use of ‘==’
    In the expression: p1 == p2
    In an equation for ‘it’: it = p1 == p2

This gives us an error. In frustration, we could define our own function for comparing people, using the equality of each field:

personsEqual :: Person -> Person -> Bool
personsEqual p1 p2 = firstName p1 == firstName p2 &&
  lastName p1 == lastName p2 && 
  age p1 == age p2 &&
  hometown p1 == hometown p2 &&
  homestate p1 == homestate p2

The Eq Typeclass

But this is tedious code. It would be a major pain to write out functions like this for every single type we create. How can we make it so we can compare people with (==)? Let’s dig a little deeper into the (==) function by looking at its type:

(==) :: Eq a => a -> a -> Bool

Notice this doesn’t specify a particular type. It uses a generic type a! This explains how it can compare both ints and strings. But why not the Person class? Let’s observe the caveat on this type signature. What does Eq a mean? We saw something similar in the error when we tried to compare our Person objects. Eq is a typeclass. A typeclass is a Haskell construct which ensures certain functions exist describing the behavior of an otherwise generic type. It is similar to an Interface in Java. The Eq typeclass has two functions, (==) and (/=). Here is its definition:

class Eq a
  (==) :: a -> a -> Bool
  (/=) :: a -> a -> Bool

These two functions are equality and inequality. We can define them for any class. In order for a data type to belong to the Eq typeclass, it must implement these functions. We make our data type a member of the typeclass by defining an instance for it:

instance Eq Person where
  (==) p1 p2 = firstName p1 == firstName p2 &&
  … as above

We don’t need to define (/=). With Eq, the compiler can determine how to implement (/=) based on our definition of (==), just by negating it with not. With this instance available, we can simply compare Person objects:

>> p1 == p2
False

Deriving Typeclasses

But this isn’t completely satisfying. Because we’re still writing out this tedious definition of the (==) function, when really it should be obvious when two Person objects are the same. They’re the same if all their fields are the same! Haskell is smart enough to figure this out if we tell it, using derived classes:

data Person = Person { …as above... }
  deriving (Eq)

Then the compiler will derive an implementation of (==) and (/=) for the Person object doing exactly what we want them to do. This is pretty awesome!

The Show Typeclass

Let’s observe some other common typeclasses. The Show typeclass is also simple. It has a single method, show, which takes the type and converts it to a String, similar to a toString() method in Java. We use this whenever we want to print something to the console. The compiler can usually derive a Show instance, just like an Eq instance. The only condition is for all the fields of an object also belong to the Show typeclass. But we can also customize it. For instance, we can use this implementation:

instance Show Person where
  show p1 = (firstName p1) ++ “ “ 
    ++ (lastName p1) ++ “, age: “ 
    ++ (show (age p1)) ++ “, born in: “ 
    ++ (hometown p1) ++ “, “ 
    ++ (homestate p1)

And we can compare the printed string for a Person in the two cases.

(Using derived show)
>> let p = Person "John" "Doe" 23 "El Paso" "Texas"
>> p
Person {firstName = "John", lastName = "Doe", age = 23, hometown = "El Paso", homestate = "Texas"}
(Using our show)
>> p
John Doe, age: 23, born in: El Paso, Texas

The Ord Typeclass

Another common typeclass is Ord. We need this typeclass whenever we want to be able to sort or compare objects. It is necessary for certain data structures, like maps. We can derive it, but more often we’ll want special control over how we compare our. For instance, the derived instance of Ord will sort our people first by first name, then by last name, then by age, etc. However, suppose we want a different order. Let’s sort them by age instead, and then by last name, and then by first name. We would define the Ord instance like so:

instance Ord Person where
  compare p1 p2 = if ageComparison == EQ
    then if lastNameComparison == EQ
      then firstName p1 `compare` firstName p2
      else lastNameComparison
    else ageComparison
    where
      ageComparison = (age p1) `compare` (age p2)
      lastNameComparison = (lastName p1) `compare` (lastName p2)

We complete this instance with the single function, compare. This function compares two objects, and determines an ordering. The ordering can be one of three values: LT (less than, meaning the first parameter comes “first”), EQ, or GT (greater than). We can see the difference in the sort order of our people using these two different approaches:

>> let p1 = Person "John" "Doe" 23 "El Paso" "Texas"
>> let p2 = Person "Jane" "Doe" 24 "Houston" "Texas"
>> let p3 = Person "Patrick" "Green" 22 "Boston" "Massachusetts"
(Using derived Ord)
>> sort [p1, p2, p3]
[Jane Doe, age: 24, born in: Houston, Texas,John Doe, age: 23, born in: El Paso, Texas,Patrick Green, age: 22, born in: Boston, Massachusetts]
(Using our Ord)
>> sort [p1, p2, p3]
[Patrick Green, age: 22, born in: Boston, Massachusetts,John Doe, age: 23, born in: El Paso, Texas,Jane Doe, age: 24, born in: Houston, Texas]

Defining a Typeclass

We can even create our own typeclasses. Suppose we have another data type called Student:

data Student = Student
  { studentFirstName :: String
  , studentMiddleName :: String
  , studentLastName :: String
  , studentGradeLevel :: Int }

Notice both the Person class and the Student class have name fields. We could combine these to create a “total” name. We could make a typeclass for this behavior and call it HasName. Then we could provide instances for both types:

class HasName a
  totalName :: a -> String

instance HasName Person where
  totalName p = firstName p ++ “ “ ++ lastName p

instance HasName Student where
  totalName s = studentFirstName s ++ “ “ ++ studentMiddleName s ++ studentLastName s

And we’re done! That’s all it takes to make your own typeclasses!

Summary

  1. Typeclasses allow us to establish common behavior between different types.
  2. Many typeclasses can be derived automatically.
  3. They allow us to use many helpful library methods, like sort.
  4. We can define our own typeclasses.

Now you can use built-in typeclasses and make your own. This opens up a big world of Haskell possibilities, so it’s time to start making your own project! Check out this checklist to make sure you have all the tools to get going!

Read More
James Bowen James Bowen

Making Your Types Readable!

So in the last post we discussed creating our own data types using the data keyword. This is an excellent way to describe most of our data. But there are more ways we should explore. We’ll look at two simpler alternatives to creating an full data type.

The Type Keyword

The first method we’ll discuss is the type keyword. This keyword creates a type synonym, much like the typedef keyword from C. We take some type, and simply give it a new name we can use to reference it.

type MyWrapperType = (String, Int, Float)

When we do this, the code’s behavior does not really change. It makes no difference whether we call the type by its new name or its old name. We can even use both in the same function! All the same pattern matching rules apply to the synonym as the original type.

myTypeFunction :: MyWrapperType -> (String, Int, Float) -> MyWrapperType
myTypeFunction (str1, int1, flt1) (str2, int2, flt2) = (str2, int1, flt1)

The type keyword is an excellent way to clarify compound types. As you write more complicated code, you’ll find you often need to have more complicated types, like nested tuples and lists. For example, suppose some function generates a 5-tuple, and a few other functions take the type as input.

complicatedFunction :: … -> ([Int], Map String Float, Maybe String, Float, Int)

func1 :: ([Int], Map String Float, Maybe String, Float, Int) -> …

func2 :: ([Int], Map String Float, Maybe String, Float, Int) -> …

func3 :: ([Int], Map String Float, Maybe String, Float, Int) -> ...

We can dramatically clarify this code with type:

type UserChunk = ([Int], Map String Float, Maybe String, Float, Int)

complicatedFunction :: … -> UserChunk

func1 :: UserChunk -> …

func2 :: UserChunk -> …

func3 :: UserChunk -> …

The best example of the type synonym in normal Haskell is the String type. String is actually just a type name for a list of characters!

type String = [Char]

One possible downside to using type synonyms is the compiler may not use your synonym when telling you about errors. Thus you need to be aware of the different renamed types in your code as you analyze errors.

Newtypes

Now we’ll discuss a different way of expressing our types: the newtype keyword. This creates a new, unique type, wrapping a single other type. For instance, we could create an Interest Rate newtype wrapping a floating point number:

newtype InterestRate = InterestRate Float

The syntax for newtype is just like creating an algebraic data type, except it can only use one constructor, and that constructor must have exactly one parameter. You can still use record syntax with a newtype. One convention is to give the field a name such as “un”-(newtype name), as follows:

newtype InterestRate = InterestRate { unInterestRate :: Float }

As with ADTs, record syntax allows you to unwrap the original float value without pattern matching. Though you can do either if you want:

func1 :: InterestRate -> Float
func1 (InterestRate flt1) = flt1 + 2.5

func2 :: InterestRate -> Float
func2 intRt = (unInterestRate intRt) + 2.5

The most important point about newtypes is they DO change compile time behavior! The following code would not work:

rate :: InterestRate
rate = InterestRate 0.5

badCall :: Float
badCall = func1 rate

func1 :: Float -> Float
func1 flt1 = flt1 + 2.5

This is because func1 demands a Float as input, but badCall passes an InterestRate, which is a different type. This is super helpful when you have numerous items which have the same underlying type, but actually represent different kinds of values.

Using Newtypes to Catch Errors

Let’s see how we can use newtypes to make a real program safer. Suppose you’re writing code for a bank. You might write a function taking a couple interest rates and a bank balance and performing some operations on them. If these are all represented by floating point numbers, you might get code looking like:

bankFunction :: Float -> Float -> Float -> Float
bankFunction balance intRate fedRate = balance * (1 + intRate + fedRate)

But now imagine trying to call this function. There are numerous ways to misorder the arguments, and the program will still compile without telling you about the error!

determineNewBalance :: Float -> Float
-- Wrong order!
determineNewBalance balance = bankFunction intRate fedRate balance
  Where
    intRate = 6.2
    fedRate = 0.5

This code will have an error you can only catch at runtime (or with good testing), since we misordered the different float arguments. We want to avoid such code in Haskell when we can, because Haskell gives us the tools to do so! Let’s see how newtypes can help us avoid this error:

newtype BankBalance = BankBalance { unBankBalance :: Float }
newtype InterestRate = InterestRate { unInterestRate :: Float }

bankFunction :: BankBalance -> InterestRate -> InterestRate -> BankBalance
bankFunction (BankBalance b) (InterestRate i) (InterestRate f) = BankBalance (b * (1 + i + f))

determineNewBalance :: BankBalance -> BankBalance
determineNewBalance b = bankFunction intRate fedRate b
  where 
    intRate = InterestRate 6.2
    fedRate = InterestRate 0.5

This code, which contains the same fundamental error, will not compile anymore! This is because we pass interest rates as the first two parameters , when the first parameter to bankFunction is actually a BankBalance. Marvelous! Note, using type synonyms would not have helped us here, because the underlying types would continue to be floats.

Summary

  1. The type keyword allows us to create type synonyms.
  2. It can allow renaming of complex types without altering compiler behavior.
  3. The newtype keyword allows us to create a wrapper around a single type.
  4. This makes it easier for us to distinguish (at compile time) between variables which have the same underlying type, but different meanings in our code.

Now that you’re ready to use types and newtypes to make your Haskell awesome, time to start making a Haskell project! Check out this checklist to make sure you have all the tools to get going!

Read More
James Bowen James Bowen

Making Your Own Data Types in Haskell

So in the last post we looked at some of the built-in types in Haskell. But one of the best parts about Haskell is that it is easy to build our own types. We do this using the “data” keyword, and then giving a type name (which must begin with a capital letter):

data FightMove = …

Type Constructors

We then describe our type with a series of type constructors, separated by the | character. Each of these also has a name that begins with a capital letter. Let’s look at the most basic type constructors we can have:

data FightMove = Punch | Kick | Block

To see these type constructors in action, let’s build a function which takes some input, and returns a FightMove. We can customize which type of move we return based on the input.

chooseMove :: Int -> FightMove
chooseMove i = if i == 1
  then Punch
  else if i == 2
    then Kick
    else Block

Notice how we use the different constructors in the different cases, but they all typecheck as a FightMove! Now you might be wondering at this point, are these type constructors like constructors in, say, Java? In Java, we can have multiple constructors that each take a different set of parameters but all produce the same type of object.

However, no matter what, the resulting object in Java will always have the same instance variables with the same types. This is not the case in Haskell! Different cases of our types can have entirely different fields. For each constructor, we are not just listing the types it is initialized with, we are listing all the fields the object will have.

data FightMove = Punch Int |
  Kick Int Float |
  Block String

chooseMove :: Int -> FightMove
chooseMove i = if i == 1
  then Punch 3
  Else if i == 2
    then Kick 3 6.5
    else Block “Please Protect Me”

So if we’re taking our type as input, how do we determine which type constructor was used? If we think back to the example with Maybe types in the last article, we remember using pattern matching to determine whether a value was Nothing or Just. But Nothing and Just are simply the two type constructors of Maybe! So we can use the same pattern matching approach for our types!

evaluateMove :: FightMove -> String
evaluateMove (Punch damage) = “Punched for “ ++ (show damage) ++ “ damage”
evaluateMove (Kick damage angle) = “Kicked at “ ++ (show angle) ++ “ degrees for “ ++ (show damage) ++ “ damage”
evaluateMove (Block message) = “Blocked while saying “ ++ message

Parameterized Types

Speaking of Maybe, how is it that Maybe can wrap any type? How can we have a Maybe Int type and a Maybe [Float] type? The answer is that types can be “parameterized” by other types! Maybe is implemented as follows:

data Maybe a = Nothing | Just a

The “a” value in this definition is the parameter of the Maybe type. This lets us use any type we want to be wrapped inside! If we wanted our user to choose the way they represent “damage” in the FightMove type, we could easily parameterize this type as well:

data FightMove a = Punch a |
  Kick a Float |
  Block String

Now we can have separate types like FightMove Int, or FightMove Float, just as we can have different Maybe types like Maybe Int or Maybe Float.

Record Syntax

As you get more comfortable creating your own types, you’ll make some that have many different fields. For instance, consider this type, which has one constructor with five fields.

data Person = Person String String Int String String

The only tool we have for accessing these fields right now is pattern matching, like we used above. If we wanted to get the values of these fields out, our code would need to look something like this:

funcOnPerson :: Person -> String
funcOnPerson (Person str1 str2 int1 str3 str4) = …

But what if we only cared about one of these fields? This code seems rather cumbersome for extracting that. On top of that, how are we supposed to keep track of what field represents what value? This is where record syntax comes into play. Record syntax allows us to assign a name to each field of a constructor.

data Person = Person
  { firstName :: String
  , lastName :: String
  , age :: Int
  , hometown :: String
  , homestate :: String }

Now that our different fields have names, each name actually acts as a function allows us to extract that value from the larger object. We no longer have to deconstruct the entire object via pattern matching to get each value out.

birthPlace :: Person -> String
birthPlace person = hometown person ++ “, “ ++ homestate person

It is also easy to create an updated copy of a person using record syntax. The following code will make a new Person that preserves the age, hometown, and home state fields of the original, but has a different name:

makeJohnDoe :: Person -> Person
makeJohnDoe person = person
  { firstName = “John”
  , lastName = “Doe” }

Summary

  1. You can make your own data types, with several different constructors.
  2. Constructors can have different numbers of parameters and different types.
  3. A type can be parameterized by other types, allowing a user to fill in what kind of type will be used inside the object.
  4. Record syntax allows us to access or change* fields of an object.

*Haskell objects are immutable, and cannot actually be changed. When you do this, you create a new copy of the object! We’ll discuss immutability more later!

If you’re super excited about making your own types and want to try out Haskell, check out our free checklist to kickstart your Haskell learning process. And stay tuned for more content here at the Monday Morning Haskell blog!

Read More
James Bowen James Bowen

Using Compound Types to Make Haskell Easier

Now that we know a bit more about Haskell’s type system, let’s get more familiar with some of the basic types. Like pretty much any other language, Haskell has integers, floating point numbers, characters, and strings. We can define a function that uses all of these basic types:

myFunction :: Int -> Float -> Char -> String -> String
myFunction x y c str = (c : str) ++ “ “ ++ show x ++ show y


myResult :: String
myResult = myFunction 4 5.5 ‘a’ “bcd”

Notice also that “myFunction” has its own type, based on what arguments it takes, and what it returns. As we know from the previous article, applying the function to arguments results in the final return type of the function, which we see in “myResult”.

Tuples

But what if we want to return multiple things from a function? Say we want to return both a floating number (“myNum”) and a string (“myStr”) from “myFunction”:

myFunction :: Int -> Float -> Char -> String -> ???
myFunction x y c str = ???
  Where
    myNum = x * y
    myStr = c : str

This is where tuples come in. Tuples allow us to combine multiple types into a single type. The type name of a tuple is denoted by a comma separated list of types within parentheses. Similarly, you build tuples with a comma separated list of the values. In this example, we would use a tuple of a Float and a String:

myFunction :: Int -> Float -> Char -> String -> (Float, String)
myFunction x y c str = (myNum, myStr)
  Where 
    nyNum = x + y
    myStr = c : str

Tuples can have as many types as you want, but many library functions only work up to tuples of 7 types. You can have the same type multiple times within a tuple as well. For instance, “(String, Int, Float)” and “(String, Int, Char, Float, Int)” are both valid types.

Most often though, two items are present within a tuple. In this case, you can use “fst” to access the first element of the tuple, and “snd” to access the second element.

addElementsOfTuple :: (Int, Int) -> Int
addElementsOfTuple tup = fst tup + snd tup

Lists

The second special type we will discuss is the List. While a tuple can contain several elements , each with a different type, a List contains many elements, each having the same type. A list type is denoted by brackets around the original tuple type name. In code, you can create lists by putting brackets around a comma separated list of values.

listExample :: [Int]
listExample = [1,2,3,4]

It is important to note, for people coming from other programming languages, that the list type in Haskell represents a linked list, not an array. It is quite efficient to separate the first element of a list from the rest, or to add an element to the front of a list. However, accessing an element in a list by index and appending two lists together are relatively inefficient operations. You can only effectively add to the back of a list using inefficient appending (++).

-- “:” is the concatenation operator for lists. It is efficient!
Add1 :: [Int] -> [Int]
add1 firstList = 1 : firstList


-- This is efficient!
getRest :: [Int] -> [Int]
getRest (_ : rs) = rs


-- This is relatively inefficient
add1ToBack :: [Int] -> [Int]
add1Toback l = l ++ [1]


-- Indexing to the 6th element. Also relatively inefficient
index :: [Int] -> Int
index ls = ls !! 5

Notice above in the getRest example that when we receive the list as a parameter, we can pattern match on the structure of the list, allowing us to easier access certain parts of it. We’ll see this pattern matching behavior a lot as we dig deeper into the type system.

Lists are ubiquitous and important in Haskell. In fact, the String class is actually implemented as a list of characters! So if you ever see an error mentioning the type “[Char]”, understand it is talking about Strings! Accordingly, there are a number of library functions which help manipulating lists. We’ll talk about these more in a later article.

The last thing to mention about lists is the existence of infinite lists. Another concept for the future is Haskell’s system of lazy evaluation. It only evaluates the expressions that it absolutely needs to. It is illegal to bring a full infinite list into scope by, say, trying to print it. But you need not bring the full list into scope to take the first 4 elements of an infinite list. Here are two ways to make infinite lists in Haskell:

infiniteLists :: ([Int], [Int])
infiniteLists = (repeat 4, cycle [1,2,3])


-- This works fine!
first4Elements :: [Int]
first4Elements = take 4 (fst infiniteLists)


-- This does not work!
printInfiniteList :: IO ()
printInfiniteList = print (snd infiniteLists)

The first element in “infiniteLists” will be a list containing nothing but 4’s, and the second will be a list that cycles between its three elements, looking like “[1,2,3,1,2,3,1,2,3,...]”
 

Maybes

The last special type we will discuss is Maybes. Maybe is effectively a wrapper that can be put around any type. The wrapper encapsulates the possibility of failure.

There are two types of maybe values: Nothing and Just. If a Maybe value is Nothing, there is no other value attached to it. If it is Just, then it contains some inner value. You typically “unwrap” a Maybe value by pattern matching, either on a function argument or using a case statement. This allows you to customize the behavior in case something failed.

resultString :: Maybe Int -> String
resultString Nothing = “It failed!”
resultString (Just x) = “It succeeded! “ ++ (show x)


resultString2 :: MaybeInt -> String
resultString2 maybeX = case maybeX of
  Nothing -> “It failed!”
  Just x -> “It succeeded! “ ++ (show x)

The cool thing about all these types is that they are infinitely composable. You can have a list of lists, a tuple containing a Maybe and a List, or a Maybe List of Tuples of Ints, Floats, and Lists! So no matter what, remember these 3 fundamental Haskell data structures:

  1. Tuples

  2. Lists

  3. Maybes

Read More
James Bowen James Bowen

Understanding Haskell's Type System

One of the things that makes Haskell unique is its strong, static type system. Understanding this system is one of the keys to understanding Haskell. It is radically different from the dynamic typing of languages like Python, Javascript, and Ruby. It’s even different in some respects from other statically typed languages like Java and C++.

Static Typing Vs. Dynamic Typing

To understand why the type system is so important, we have to first consider the advantages and disadvantages of static typing in the first place. One important benefit of  static typing is that many bugs are found at compile time, rather than run time. With a dynamically typed language, you might ship code that seems to work perfectly well. But then in production a user stumbles on an edge case where, for instance, you forget to cast something as a number. And your program crashes.

With static typing, your code will simply not compile in the first place. This also means that errors are more likely to be found where they first occur. An object with the wrong type will raise a compile issue at its source, rather than further down the call chain where it is actually used. As a result of this, static typing needs a lot less unit testing around type agreement between methods. It doesn’t excuse a total lack of testing, but it certainly reduces the burden.

So what are the disadvantages of static typing? Well for one thing, development can be slower. It’s oftentimes a lot easier to hack together a solution in a dynamically typed language. Suppose you suddenly find it useful to do a database operation in the middle of otherwise pure code. This is very hard in Haskell, but easier in Javascript or Python.

Another disadvantage is that there’s more boilerplate code in statically typed languages. In languages like C++ and Java you declare the type of almost every variable you make, which you don’t have to do in Python. However, this is isn’t a big a problem for Haskell, as we’ll see.

Haskell and Type Inference

The first step in truly understanding Haskell’s type system is to remember the fundamental difference between functional programming and imperative programming. In imperative programming, we are giving the program a series of commands to run. In functional programming, our code actually consists of expressions to be evaluated. Under Haskell’s type system, every expression has a type.

Luckily, unlike C++ and Java, Haskell’s most widely used compiler (GHC) is generally quite good at type inference. It can usually determine the type of every expression in our code. For instance, consider this function:

func baseAmt str = replicate rptAmt newStr
  where
    rptAmt = if baseAmt > 5 
      then baseAmt 
      else baseAmt + 2
    newStr = “Hello “ ++ str

In this function, notice that we do not assign any types to our expressions, as we would have to with Java or C++ variables. The compiler has enough information to know that “baseAmt” is an integer, and “str” is a string without us telling it. Further, if we were to use the result of this function somewhere else in our code, the compiler would already know that the resulting type is a list of strings.

We can also see some consequences that the type system has on code syntax. Because every expression has a type, all if statements must have an else branch, and the result of both branches must have the same type. Otherwise the compiler would not, in this example, know what the type of “rptAmt” is.

While we can avoid using type signatures most of the time, it is nonetheless standard practice to at least give a signature for your top level functions and objects. This is because the best way to reason about a Haskell program is to look at the types of the relevant functions. We would write the above code as:

func :: Int -> String -> [String]
func baseAmt str = repeat rptAmt newStr
  where
    rptAmt = if baseAmt > 5 
      then baseAmt 
      else baseAmt + 2
    newStr = “Hello “ ++ str

In fact, it is not unreasonable when writing a Haskell program to write out the types of every function you will need before implementing any of them. This is called Type Driven Development.

You could go further and also give every variable a type signature, but this is generally considered excessive. We can see how much clunkier the code gets when we do this:

func :: Int -> String -> [String]
func baseAmt str = repeat rptAmt newStr
  where
    rptAmt :: Int
    rptAmt = if baseAmt > 5 
      then baseAmt 
      else baseAmt + 2
    newStr :: String
    newStr = “Hello “ ++ str

Functions as Types

In Haskell, functions are expressions, just like an integer or a string. Hence, they all have types. In the example, we see that “func” is a function that takes two arguments, an Int and a String, and returns a list of Strings. We can apply a function by giving it arguments. Function application changes the type of an expression. For instance, we could fully apply our function by providing two arguments, and the resulting type would be a list of Strings:

myStrings :: [String]
myStrings = func 4 “Hello”

However, we don’t have to apply ALL the arguments of a function at once. We could apply only the integer argument, and this would leave us with a function that still required a String. We could then call this partial function with the one missing argument:

partial :: String -> [String]
partial = func 4

full :: [String]
full = partial “World”

Conclusion

So if you’re new to Haskell, this was probably a lot of information, but here are a few main takeaways to remember:

  1. Haskell is a statically typed language

  2. Every expression in Haskell has a type, including functions and if statements

  3. The compiler can usually infer the types of expressions, but you should generally write out the type signature for top level functions and expressions.

Read More