When you start to learn Haskell, many people will tell you it’s hard. When you say, “Oh, it is a bit hard because I have no background at all in programming,” many people imply that that might actually make it easier. I think the idea is that, since functional programming languages are so different from other languages, it might be easier to start somewhat tabula rasa and not have to retrain your brain to think about programming in an entirely different way. To me, it’s a bit like saying that if you already know English, then learning a second language will be more difficult for you than if you didn’t know any language.
I can see why one might think this, but I think it understates the difficulties that a nonprogrammer learning Haskell as a first programming language faces. When I started about 5 months ago, I was a sort of guinea pig for my teacher, an experiment to see how different it would be to teach a nonprogrammer (I don’t have any kind of math background to speak of either and have not had a math class in >20 years). Indeed, when I told him I was going to write this blog post, he confessed he hadn’t, in the beginning, really expected me to understand anything at all. I am glad he didn’t tell me that before (he has always appeared much more confident in my ability to learn Haskell than I have been, because he believes nearly anyone can). I am reasonably confident about my intellectual capacities, but I also know my weaknesses, and during the first couple of months, I thought there was no way in hell I would ever learn Haskell to any kind of competence. I hit a learning curve approximately like this:
Only I didn’t have those abs.
The first and most obvious problem with deciding to learn programming with no experience actually has nothing to do with Haskell. True Confession: I didn’t know Github existed until I started learning Haskell; I didn’t even know what version control was. So, as I was starting with Haskell, I had Linux, Git, an invitation to join an IRC channel (a wut?) and, er, “a text editor” thrown at me. Seems silly, yes? Well, when your primary use of computers has previously been Googling recipes and letting your kids play Minecraft, and all of a sudden someone tells you to open up your “REPL” (whatever that is–yeah, yeah, I know now) and your text editor, you…try really hard to pretend you know how to do those things while Googling furiously. I still have more things to learn about Linux and git–a lot more–along with learning Emacs, but they are mere constituents in a continually growing pile of stuff I still need to learn. Anyone who wants to learn programming has to learn all these things at some point, but programmers starting to learn Haskell wouldn’t.
If you successfully figure out how to get your terminal, text editor, and REPL going and push something to a Github repo (and then hear, “Well, you should write the text files in markdown,” and start rending garments), you hit the second wall: all the Haskell materials I’ve seen assume some background in programming and, often, mathematics as well. Learn You a Haskell for Great Good is usually recommended to inexperienced programmers wanting to learn Haskell because it is written in a friendly and approachable way. However, it says clearly in the introduction that even it assumes you have some experience with imperative programming languages, and that assumption shows. I’m taking the FP101x class through edX right now, and it also assumes you have at least a year of programming experience. Well, what’s wrong with that? There are two problems, from the perspective of a nonprogrammer. The first is the vocabulary. What is a compiler? What is “statically (as opposed to dynamically) typed?” What are “imperative” or “object-oriented” languages* and how are they different from what I’m trying to learn? What is “syntactic sugar?” Why is printing something considered a “side effect?” What are these ‘foo’ and ‘bar’ functions? Most of these concepts are familiar, though, to anyone with programming experience.
The second problem is that many of these materials try to teach functional programming concepts by analogy to imperative language features. Even leaving aside the question for now of whether that is an effective way to teach those concepts**, if you don’t know the imperative language concept used for the analogy, then the whole thing is more confusing than helpful. I don’t understand how other languages loop over lists, not really, and so explaining list functions in Haskell in terms of looping is confusing and unhelpful, especially because as a former syntactician, I was already familiar with recursion.
A final, related problem is what I call the Lack of Purpose problem; learning about types is great, but I didn’t know what to do with it all. I had no way to apply most of what I was learning. Programmers, even inexperienced ones, have ideas of what kinds of things you are likely to want to accomplish with computer programs, and often have some understanding of the kinds of steps you could reasonably break those goals into. Nonprogrammers are less likely to have that foundation. Yes, I use computers to accomplish a variety of tasks, but I usually have little idea of what else might be possible or what kinds of functions might make that happen. Look, don’t make fun of me. I know. But it’s just something that never crosses my mind in my daily life because I’m not a programmer.
Try to imagine, for comparison, that you are not a welder. You don’t really think too much about where one would use welding or what kinds of problems it might be used to solve. If someone suddenly launched into a spiel about the physical properties of acetylene welding, so you could understand the theory of it, you might think to yourself, well, this is all very interesting, but what would I ever use this information for? It is more difficult to retain and assimilate information you have no immediate use for.
For me, this problem has been gradually ameliorated because my teacher patiently walks me through reading code. After he wrote this handy URL shortener, he spent a couple of hours explaining the code to me, line by line. We’ve done similar, albeit shorter, exercises of this sort on other occasions. Finally, I am starting to get a sense of what purpose I might put Haskell to and what kinds of steps I would need to follow to do that; finally, functions and types have a context. So, the learning curve is starting to decrease in severity now, but the initial difficulties meant I almost didn’t make it. I wouldn’t have without a teacher who invested large amounts of time and reassured me I could do it if I just stuck with it and did the work.
Most of the difficulties I had in the beginning are not the things that experienced programmers would find difficult. That doesn’t mean they don’t have their own issues to overcome, like learning to think about programming in a new way, but it is a different experience.
* Honestly, I still don’t really understand why people like imperative and OO languages. Mutation might be unavoidable at times, but hey if you read what I’m linking to here, it seems better to avoid where possible. (EDIT to appease Hacker News nitpickers. Is this the only part of this essay y’all read or just the only part you respond to? Because, seriously, those responses are weak. Up your game, Hacker News.) Why do you want so many, seemingly poorly controlled side effects? Why don’t you want strong typing? Well, to each his own, I guess, but these things seem prima facie to decrease the safety of and ease of reasoning about programming.
**In my experience teaching human languages, I suspect the analogy hurts more than it helps. The goal for fluency in a human language is to get you thinking in the target language, not translating in your head, a process that a) slows you down considerably and b) leads to a lot of mistakes. English speakers learning Irish Gaelic, e.g., make word-order mistakes all the time. Trying to speak Japanese by analogy with English means learners frequently think suki (‘like’) and hoshii (‘want’) should be verbs, leading to syntax errors. I suspect it’s the same with computer languages: learn to think in the target language in the beginning (or as soon as possible) for much long-term benefit. Of the two footnotes, this is the one I wish people would engage with.
i’m not a programmer and I’ve chosen Haskell as my first language and I’ve encountered a lot of the road blocks you mentioned. You’re very right about those Haskell learning tools like Learn You A Haskell; they are not meant for beginners.
I think you’re also right about not coming with a blank tabula rasa. When I read some of the posts out there about Haskell, I get the distinct impression that there’s a huge process for a lot of programmers going from imperative to functional as they switch paradigms.
I’ve found FP Complete’s School of Haskell the most useful, so far. It has been, though, slow going, as I struggle to wrap my head around basic concepts.
Good luck and I hope you keep at it.
LikeLiked by 1 person
So good to hear from a fellow traveler! Thank you, and good luck to you as well.
LikeLiked by 1 person
Great post.
I hadn’t considered how the assortment of tools can add to the difficulty in learning *any* programming language. It’s definitely a lot to take in all at once. I too thought that, in many ways, having no background in programming could actually be a good thing when learning Haskell because, as you said, FP is so different from most languages people are coming from. But I see now that while I may have *tried* to forget a lot of things I knew, I didn’t forget how to use git, an editor, the command line, the notion of a compiler, etc – all stuff that is practically required if you’re going to be writing code. I think you’re right that the tabula rasa idea isn’t practically true.
LikeLiked by 1 person
> A final, related problem is what I call the Lack of Purpose problem; learning about types is great, but I didn’t know what to do with it all. I had no way to apply most of what I was learning.
I’ve been programming for many years, but I still remember feeling like this in the distant past — I started with Python, read some tutorials, and thought “That’s nice. But how do I do stuff?” And then I dropped programming for some amount of time — six months, a year and a half? It’s been too long to remember the specifics.
LikeLike
You’re TOTALLY the same as me! I had *heard* of GitHub, but had no clue how it worked or what Git was. Had to learn what vim was, how to do basic terminal commands etc. But one BIG difference: I found an actual introduction to Haskell that is *truly* accessible to beginners with NO background. It’s the Haskell Wikibook! It’s actually phenomenal and thorough! https://en.wikibooks.org/wiki/Haskell
Now, despite being mostly good, a few elements of the Wikibook were a bit confusing. BUT THEY ARE FIXED NOW because I was able to edit the book and improve it when I figured out what was confusing!
Note: the whole Beginner’s Track is what is excellent. The rest is mixed, some great, some I still have no understanding or or haven’t fully read.
But seriously, people should be recommending the Wikibook over Learn You A Haskell when it comes to complete programming beginners (or maybe anyone). And if *anything* about the Wikibook is inadequate, we can make it better! Learning by teaching, by editing, that’s a great way to go.
And now I still don’t really program that well, but I get the idea, and I’m working to make my main project presented in a way that would be truly accessible to someone like me!
LikeLike
Hihi, this is EXACTLY how I started 20 years ago, being a political scientist. :)
It just wasn’t Haskell.
But oh I remember the total meaninglessness of all the jargon so well, having been told to “just edit the makefile”, not being able yet to quit vi, understanding what the hell is a loop supposed to be..
And dammit, I read and understood Hegel.. ;)
So no worries, you’ll get there and your non-computing background might even play out as an advantage…
I promise you can really learn this stuff!
LikeLike
Don’t even get me started about reading Hegel. Heh.
LikeLike
Imperative languages could be viewed as historical accident due to the early slowness of computers. All computers are imperative underneath – behind the scenes, Haskell is mutating things and causing side effects for you. The first language was basically assembly code (“add 1 to the number in the working register, save the result in this other register”), which was imperative too, because it was only a slightly higher level of abstraction than the hardware.
C was built on assembly, and it was imperative too. A big reason was probably that they wanted to give programmers maximum control over the (imperative) assembly generated from their source code. Computers were far slower in those days, so there was no room for wasted computation. The advantage of imperative code is that it is easier to see how to speed it up – you can see what code is run in what order, and you just need to remove or optimize some of those operations. Whereas if C were functional, programmers might have complained that they didn’t know how to make their program fast enough, so they wouldn’t have used it.
Later, more abstract imperative languages probably use this rationale for being imperative, to some extent. And part of the reason might be that the language creators were just used to imperative thinking, because C is imperative, and so they didn’t think of other styles.
Finally, some problem domains are all about mutation – mainly video games. Using a functional programming language where mutation is harder is counterproductive – imperative languages are easier for such programs. Though this traditional wisdom may change as more patterns and specialized abstractions for functional game development are found – perhaps eventually functional languages will become the new standard for game development.
LikeLike
LISP is 15(?) years older than C. If you look at the time and resources used to sell various chipset/OS/languages you can see the rise of imperative languages has more to do with the corporations that promoted them and the chips they ran on than any other factor.
LikeLiked by 1 person
LISP is imperative. You can look at some early LISP code, for example in http://www.softwarepreservation.org/projects/LISP/book/III_LispBook_Apr66.pdf, and see many mutable variable assignments and loops dome in imperative way.
LikeLike
What did you think of tryhaskell.org? I wrote that for programming newbies and tested it on a few friends who don’t know what a text editor is. I’m planning on making a complete book in this format which basically teaches you by asking you questions (many more than the one or two on tryhaskell.org; one can consider that the prototype) from the ground up.
LikeLiked by 1 person
tryhaskell.org is a great tool for starting out. It was the first thing I ever did in Haskell, when I was still not sure if I wanted to learn Haskell or not. Without having that to play around with, I don’t know if I would have taken the plunge, quite honestly. It looks like it’s on the new Haskell home page, too, so I’m really happy to see that.
I think a book structured that way would be wonderful; that could really take care of the Lack of Purpose problem and show off the beauty of Haskell’s abstractions.
LikeLike
AWESOME ! Wish there was explanations for each line on that URL shortener. I think that is a basic problem with all non programmers, you cannot traverse the abstraction. No one can tell you exactly how one thing goes to the next. You just get magic everywhere.
LikeLiked by 1 person
I’m writing up a literate version of the URL shortener as we (metaphorically) speak.
LikeLiked by 1 person
Here’s the literate version of the URL shortener: http://bitemyapp.com/posts/2014-11-22-literate-url-shortener.html
LikeLike
I forwarded this comment to the author of the URL shortener, and he (and I) wrote it in literate Haskell with commentary. The level of detail in different sections is a little uneven, intentionally. Explaining what it means to qualify imports was more reasonable for this post than explaining in detail what monad transformers are. But we plan to do more of these literate Haskell code commentaries (I don’t think he had realized how much that exercise helped me). Anyway–here it is http://bitemyapp.com/posts/2014-11-22-literate-url-shortener.html
We welcome feedback about it as well. Thanks!
LikeLike
This is a very inspiring article and it’s great that you stuck with it!
I’m coming to Haskell from an imperative background and for me it hasn’t been too much of a challenge. However, I do remember starting off and how all these tools and vocabulary is daunting at first.
I think over the years learning to program has gotten easier in terms of the resources available, but there is still a lot of ground to cover to make more accessible. And of course, it should be as accessible as possible, especially now that the influence of software is become pervasive even in the main stream world.
LikeLike
“Honestly, I still don’t really understand imperative languages. Why would you want things to mutate? Why do you want so many, seemingly poorly controlled side effects? Why don’t you want strong typing? Well, to each his own, I guess, but these things seem prima facie to decrease the safety of and ease of reasoning about programming.”
Figuring out how to make programing in an imperative Object Oriented language sane involves so many pseudo rules and incantations, that once you learn them, it is very hard to imagine that you can get things done without things like mutations. People think I am joking when I say they do not need mutations. This is why (I think) people believe it might be easier to learn from a blank slate then to unlearn and retrain.
Thanks for the posting, and you are lucky to have a teacher like Chris!
LikeLiked by 1 person
If you want to understand why imperative programming matters, you only have to read this paper (discussion). Some algorithms are most naturally expressed through mutation, and a pure language can sometimes make it awfully hard to correctly express processes that are conceptually simple (here’s another example that you can compare with the imperative version).
Mutation itself is not a problem until it is combined with reference semantics. Reference semantics leads to uncontrolled sharing, where any mutation can have arbitrarily distant impact, and that’s where you lose the ability to reason locally about mutations.
LikeLiked by 1 person
Re: What is a compiler?
The first time I started to take a programming course was in the 80’s. Many of the students had some previous access to early personal computers, but I had not. When the instructor mentioned that it would be necessary to run the code we wrote through a “compiler”, I thought he was referring to a separate machine somewhere on the campus. I assumed you had to store your code on a floppy and take it to the place where the compiler was. I imagined the compiler must be a roomful of complex electronic equipment like something out of a science fiction film. I had to ask “where is the compiler?”. As you can imagine answers like “the executable should be on your path” were incomprehensible. What path? You mean the one that goes over to the library?
LikeLiked by 1 person
That is an awesome story. I am completely sympathetic.
LikeLike
I understand where you are coming from. I am learning Ruby/Ruby on Rails at the moment but from what I have found having a good foundation is the language doesn’t even begin to help because like you said, what would I actually do with this. Instead I have taken the approach of a toddler: observe, mimic, repeat, and get really excited about the little things that I know. There are a lot of resources out there and I do as many projects that I can do so that I work with a whole array of tools that are required and not just language specific jargon. It helps to know that whole gamut of eclectic tools because at some point or another you will need those too.
LikeLike