# What 'procedural' habits to break when teaching 'functional' programming?

I have a class undergraduate students (2nd and 3rd year) who have had at least two terms of college/university level programming courses using a procedural programming language (typically C++), and often a term or two in other languages, also procedural. They have usually, though not a requirement, taken programming in high school, which was also based on one or another of the procedural languages. In the other computer classes they might have taken, or be taking concurrently, they have a procedural language as a secondary exposure. For example, the web design classes use JavaScript and PHP extensively. Bottom line is that these students are well versed (indoctrinated) in the imperative paradigm.

Now I have to teach them functional programming, and help them to adjust to a new way of thinking about the problems and solutions. In other words, I want them to be able to write a Scheme program, not a C++ program translated into Scheme.

So then, the question here is this: in the switch from procedural to functional programming, what are the critical "habits" to change in the students' minds so that they are able to grasp the functional paradigm enough to get them programming in the new language rather than writing old programs in the new style?

• It does not directly answer your question, but I highly recommend the introduction of Hughes' Why functional programming matters on differences in programming paradigms. cs.kent.ac.uk/people/staff/dat/miranda/whyfp90.pdf
– user24
Sep 5, 2017 at 15:15

I undertook this study myself about a year ago. I started working through the Programming Languages MOOCs on Coursera (Part A Part B Part C), which are based on a UW course of the same name.

I didn't really know what I was getting into, but it sounded interesting. Before I knew it, I was learning a radically different approach to programming first with SML and now with Racket (Ruby is up next).

Here are some of the key ideas that were a major departure from my having programmed in C and Python:

• Avoid mutation. Over and over again the professor emphasizes why mutation is a bad thing (or at least potentially problematic). From the world of C where I learned about declaring and updating variables almost immediately, this forced me to think differently about the information my programs contained and processed.
• Don't call them variables. As a continuation of the above, they aren't variables (because after all they shouldn't vary because they don't mutate); rather, call them "value bindings." Moreover, these value bindings exist in an environment. This latter idea is essential (and has only recently sunk in) for understanding function closures.
• Think recursively. Of the 3+ assignments I've completed for Part A and Part B, thinking recursively has been an essential component for creating elegant, efficient solutions. Indeed, in some cases it's been the only way to solve a problem thanks to the goal of avoiding mutation. This is not to say that recursion doesn't exist in other languages (because it obviously does), but it's been a more fundamental element to the design of solutions using functional programming languages.

Those are my big three; I'm sure there are more. Obviously understanding anonymous functions, higher-order functions, first-class functions, and all that comes with them (e.g. currying) is what functional programming entails. However, this content wasn't a "rewiring of old habits" in the same way that the above points were.

For what it's worth, I appreciate what C has taught me about what happens on a lower level, but I think studying a functional language qua an approach to programming has made me an overall better programmer.

When I created my course for Scheme (under not dissimilar circumstances from yours), I used The Little Schemer as my primary source. The book itself rapidly became too dense for my HS students, so I designed my lessons around each chapter.

My course, then, like the book, took the students slowly through a very foreign landscape. We took no effort to translate imperative programming at all. Instead, we were consing and cdadring up a storm, playing with lists, playing with atoms, and creating tiny programs in this alien (elephant-ridden) landscape together.

There was a downside to this, however: because it was presented in a truly functional paradigm, my students did not advance quickly, and did not become nearly as capable in Scheme as they were in Java, Javascript, or C. They just couldn't get the breadth and depth of experience within the functional idiom over the course of two months that they could with imperative programming in two years. Nevertheless, the work that they did truly was native Scheme.

I taught a "Functional Programming" course on multiple occasions. It was basically a Haskell course. Phew, can't wait to teach it again.

To answer you question precisely: you have no state, no classes and no usual OOP model. You have to abstract things much more than you are used. Oh, and there is this weird syntax. I mean---

uncurry f a b = f (a, b)


But that's all typical beginner's problems. In my experience, any computer science student worth her salt would overcome this. The real hurdles start later. My answer gets a but Haskell-focused, sorry.

The first killer are monads. Actually, I introduce first type classes, which are sometimes a bit hard to grok, and then enter monads. You can feel the class dropping their high spirit at this. Even if I am quite proud with my way of coping with monads in this class.

It is basically the equivalent of pointers, if the old spoken take in CS is to be trusted. Pointers are hard and there are students who get stuck on them for longer. Same with monads.

Then, if you can do both "normal" style Haskell and monadic, you are good for a prolonged period of time. You have state now. You can do IO. By the way, State was also a quite hard thing to warp their mind around for the students, but I'd toss it into the "monad" box.

What else? Arrows feel like a yet another "monadic" hurde. And they most surely are. But by then the students are quite battle-hardend and have not so many problems.

What I would expect to be a problem, but what was not, were higher-order functions. All these operations on lists and such. And lambdas on the level of a language construct. The lambda calculus is a fully different story, also hard. But that's probably a deviation, as it was a quite theoretical block in an else quite practical course.

A bit problematic, but that's an accomodation problem in my opinion is the applicative programming and all that point-free code golf in Haskell. But if done in moderate amounts and properly explained, students mostly find their fun in it.

• Welcome to CSE Stack Exchange! You obviously know a lot about Haskell, but you may want to focus your answer more on procedural habits to break, rather than the most difficult concepts to teach in Haskell. Sep 1, 2017 at 22:58
• Thanks. The somewhat hidden message of my answer was exactly that breaking habits happens in the beginning of the course, I would say, first quater. But the problems with learning a functional language do not stop there. To name such a "procedural/OOP" habit: syntax, of course! Sep 1, 2017 at 23:28

"The real voyage of discovery consists not in seeking new landscapes, but in having new eyes." -- Marcel Proust
The first language I had to use in College (BS Engineering curriculum) was APL. The next was Lisp. My prior exposure was mainly Apple ][ BASIC and a bit of 6502 assembler, and some PDP-11 scripting.

So, my view is that you don't need to undo any procedural habits, if the language is so wildly different from procedural that the students are entirely lost in a new landscape with no familiar landmarks whatsoever. It worked for me.

• This answer is worth more than my +1! Would be worth a bounty if the author had not jumped ship 😑 ie For the developer to write his C# or C++ or whatever more functionally is one thing For the teacher the break should be really decisive not piecemeal
– Rusi
Jan 31 at 16:56