12
$\begingroup$

We've recently gone through the process of selecting an intern from a local university to join our team for the summer. We are using F# and almost exclusively using functional programming techniques on our team, and finding a candidate with any exposure to functional programming proved to be impossible. This isn't that surprising, given that even several senior members of the team with Master's degrees didn't have much exposure to functional programming before joining our team. However, I've been wondering why so few undergraduate programs teach functional programming concepts.

Having attended a university that taught C/C++/Java for 90% of my undergraduate classes, I found it strange when first learning Haskell that the language was never used in any of my classes, especially considering how much easier and more obvious it makes the implementation of fundamental data structures like trees and lists. When building enterprise-scale systems, avoiding things like state mutation becomes critical. So why aren't functional languages taught first, and functional programming techniques taught as a "use this approach first, when possible" with imperative techniques and things like explicit memory management and use of mutable state taught as "only use this when absolutely necessary for performance reasons"?

I would think, with the increasing move towards multi-core processors, and the growing relevance of distributed systems, that teaching functional programming from the beginning would be the best way to prepare students for successful careers in the industry. Is there a reason that universities avoid teaching Haskell or other functional languages first (or at least including them as a major part of the undergraduate curriculum) and teaching imperative programming as a supplementary skillset?

$\endgroup$
7
  • $\begingroup$ Welcome to CSEducators. You seem to be simply asking for opinions, rather than how to teach. We try to put more emphasis here on how to do things. Maybe the situation about FP will change. $\endgroup$
    – Buffy
    Commented May 12, 2018 at 23:49
  • $\begingroup$ Thanks, can you suggest a better place for this type of question? $\endgroup$ Commented May 12, 2018 at 23:50
  • 2
    $\begingroup$ I don't really think employ-ability would be affected. Learning FP first would make you better at any language, because you would more instinctively avoid shared mutable state, better encapsulate effects in the type system, etc. Also, few people end up working exclusively in the languages they learned in school. Learning new languages on the fly is par for the course in a programming job. This is more about what fundamental techniques are the right ones to learn first. $\endgroup$ Commented May 13, 2018 at 2:15
  • 1
    $\begingroup$ It's probably because functional programming is still an esoteric skill, so there are a limited number of people who can do and teach it well. $\endgroup$
    – pojo-guy
    Commented May 13, 2018 at 3:57
  • 1
    $\begingroup$ We should note this related question: cseducators.stackexchange.com/q/3558/1293 $\endgroup$
    – Buffy
    Commented May 13, 2018 at 18:58

6 Answers 6

8
$\begingroup$

The why isn't necessarily related to the relative quality or benefit of the various approaches to the first courses. But some of the comments given to the question capture part of the reasons.

First, FP isn't precisely mainstream in industry. Most programming in the real world is now is done in Dijkstra-derived languages, not McCarthy-derived ones. This may change, of course, as the world starts to recognize the benefits of FP as it previously did for OOP. As the questioner mentioned FP has advantages in some important areas. But most curricula take a look at who is hiring entry level folks and what they want to see in the person's background.

Another reason is that most of the faculty is much more knowledgeable about those mainstream things than others. If the faculty went through a long program in which almost everything is built in Dijkstra-derived languages that is what they are more likely to teach beginners. OTOH, faculty (including some folks here) who went to, just to pick a name, MIT, are more likely to start in FP, but may still be constrained by the first reason.

Most faculty still think of FP as an advanced topic and so Let and Lambda tend to come late in the curriculum, after the students have seasoning. As you suggest this is not necessary, and may not be desirable, but there is a lot of inertia.

There is always some mental disruption when a student is asked to change paradigms, since a paradigm is built on a consistent mental model that is also relatively closed, not needing outside explanations. Paradigm shift is, in general, difficult, even for experts. The HS or undergraduate curriculum is short in time so most faculty are resistant, at least a bit, to require uproar in the middle of the two or four years. Much better, it is normally thought, to treat one paradigm as the main-line and any others as nice but inessential to the main goal.

If you have a lot of chickens, you get a lot of eggs. OTOH, if you have a lot of eggs, you get a lot of chickens. Pigs, not so much.

However, if you are going to teach any given paradigm, you need to do it well. You need to make students completely comfortable in that paradigm so that when presented with a new problem they aren't confused about how to begin or what should be the next step. Languages/paradigms should fit like a comfortable pair of shoes. I'm not certain that current OOP education is as good as it could be, actually, but that is due to my own background. If we suddenly adopted FP widely, you might well think the same thing about the then state of the art concerning FP. Change happens, but it is slow and is resisted.

Students need two things at least. They need to be able to solve problems so that they can be successful in academia and/or the world of employment. But they also need to be able to think and be flexible so that they are able to move with the times as necessary. So we tend to teach one paradigm completely (for the first goal), but allow them exposure to others (for the second). Mostly it works.


I'll also note that learning new languages is actually harder than it is often represented to be. If two languages are within one paradigm and have similar concepts then it may seem to be merely a question of new syntax applied to old ideas. Java and Python have similar ideas but quite different syntax. But learning the syntax of Python as an experienced Java programmer won't make you pythonic. But changing paradigms is much, much harder, even when the syntax is nearly the same. I'll note that C++ was derived from Dijkstra's ideas primarily (via Simula), whereas Java's core concepts come from Alan Kay. If you don't understand that then you can wind up programming badly in both languages.

$\endgroup$
17
  • $\begingroup$ Good points about the inertia and meeting expectations for entry-level positions. I wonder if it still might be beneficial to teach both paradigms in introductory courses, so that when it comes to learning basic data-structures, like implementing a red-black tree, the student can implement the tree in a language like ML or Haskell, where inductive types are simply defined in a few lines of code, as opposed to implementing the tree in C or Java, where it would be much more verbose and the functionality of the tree would be obfuscated by the mechanics of the language. $\endgroup$ Commented May 14, 2018 at 17:20
  • 2
    $\begingroup$ I think a bad idea. Students can wind up thrashing mentally. Paradigms, if they truly are are distinct. Their mental models can conflict. Give students a solid base in one before you try to teach them to "think different". Then you can be more clear about the advantages of each. $\endgroup$
    – Buffy
    Commented May 14, 2018 at 18:28
  • $\begingroup$ >>> I'll note that C++ was derived from Dijkstra's ideas primarily (via Simula), whereas Java's core concepts come from Alan Kay. Hm. Can you give an example? $\endgroup$
    – paus
    Commented Dec 28, 2018 at 18:11
  • $\begingroup$ @paus, I don't understand what you desire. Stroustrup explicitly mentions Simula in his writings as a major influence. Java, like Smalltalk (inspired by Kay) is a class-based language with objects on the heap. $\endgroup$
    – Buffy
    Commented Dec 28, 2018 at 18:13
  • 1
    $\begingroup$ I think that most of the students have an experience like the following: "Once we tried to write something with our friends in the class on C++/Python/PHP, as hard as we could, and based on our experience we got it right". Although all my generation started from BASIC and Pascal, I don't think that they had an impact on the understanding of deeper principles, they only gave basic concepts. Other stuff we had to drink from industrial languages, not educational ones. $\endgroup$
    – paus
    Commented Dec 28, 2018 at 18:34
5
$\begingroup$

I found it strange when first learning Haskell that the language was never used in any of my classes, especially considering how much easier and more obvious it makes the implementation of fundamental data structures like trees and lists

It might make implementing some data structures easier, but not all -- for example, I don't really think Haskell would make implementing arraylists or hashmaps necessarily easier.

(Also: I think implementing trees and lists is relatively simple in languages like Java or Python. The hard part, IMO, isn't teaching students how to implement trees or lists: the hard part is showing them how to manipulate them in arbitrary ways while avoiding edge cases. That's going to be challenging whether you approach the problem imperatively or functionally.)

When building enterprise-scale systems, avoiding things like state mutation becomes critical.

But what if you want to implement something like a video game? Mutation is an integral part of programs like those. Of course, there are ways to restructure your code to avoid mutation, but most existing literature, tools, libraries, game engines, etc... related to gamedev don't take such an approach.

Or what if you want to go into embedded systems or security and end up needing to mess around with a lot of assembly or C? For better or for worse, you're going to end up spending a lot of time working with lower-level languages: functional languages won't necessarily end up being relevant to your work.

Or what if you're interested in scientific and mathematical computing? Again, I don't think functional languages are really widely-used here: languages like Python, R, Julia, C, or maybe even Matlab and Fortran are more ubiquitous for a variety of reasons.

Basically, a CS curriculum is supposed to prepare students to excel in a wide variety of backgrounds, not one specific area. For that reason, I don't know if it's necessarily correct to assume that all (or even most!) students will move into working on enterprise software and the like.

In particular, I think many "intro to CS" courses are targeted to be useful for people who may not necessarily want to move into computer science but still want to learn coding as a skill. In that case, you'd probably want to teach them a more mainstream language.

(You could also split up your "intro to CS" course into a "intro for non-majors" and "intro for majors", but that would introduce some overhead, both for students and teachers.)


But of course, none of what I said above directly pertains to the heart of your question. I'm making the argument that a school shouldn't teach only functional programming, which isn't what you're asking: why don't we start with functional programming first?

Here, I'm personally not convinced that Haskell-style languages that push for immutability and clear containment of side-effects are actually going to be approachable, or even useful, for absolute beginners.

If somebody has zero background in programming and tech, one of the best ways of "hooking them in" is to have them start writing programs that actually do interesting things as soon as possible. That's why so many introductory tutorials these days start with webdev and JavaScript, why logo (and modules like 'turtle' in languages like Python) are such a popular choice, and so forth.

More recently, there's been a trend to start incorporating actual hardware into intro classes -- using things like micro:bits, for example. Or to rephrase: a trend of finding ways of giving the student more interesting ways to mutate state: LEDs, stepper motors, electronic components, and so forth.

Contrast this with languages like Haskell: in order to do anything that interacts with the world, you need to start by explaining how the IO monad works (or find some convincing way of handwaving it all away).

While this isn't an insurmountable challenge, it is a potential stumbling block and is sort of annoying, especially if you're trying to ramp students up to doing interesting things as soon as possible. And the more you can improve potential stumbling blocks, the better.

A little more broadly, I think immutability and containment of side-effects can be very useful when working on large-scale programs, but they seem sort of gratuitous and unnecessary when working on smaller ones: it's challenging to demonstrate why they're useful, especially in intro when they have no context into what the alternative would look like.

(IMO this is also why many schools struggle with effectively teaching students how to use version control tools like git: even if you ask students to work in teams on a long-term project, you don't really need to use things like branches and can get away with just pushing directly to master.)


Basically, while I'm a fan of functional programming, it's hard for me to see how exactly you'd go about teaching it to beginners. I'm sure that if I were really pressed, I could figure something out, but it's non-obvious to me how I'd teach the material in a high-quality way and how I'd rearrange certain topics I think are essential.

(For example, many students struggle immensely with the notion of "indirection" -- basically, how to use things like references and pointers. But if we're using an immutable language, indirection suddenly isn't as relevant: a variable could be a reference to some data or a copy of said data: it makes no difference. In that case, how and where should I teach students about indirection?)

Taking all of the above into account, I think a more tractable alternative is to start with a language like Python (or maybe Java?) first, then move on to more challenging languages like C or Haskell second.

$\endgroup$
2
  • $\begingroup$ "But what if you want to implement something like a video game? Mutation is an integral part of programs like those." – I believe that is a common misconception. "Super Monao Bros" is a Jump&Run Clone in Haskell. I once read an interesting blog series about Purely Functional Retrogames. Idris as a language is not only purely functional (no side-effects) but also total (always terminates, i.e. no endless recursion, no endless loops, and thus not Turing-complete), and one of the example programs is an Asteroids clone. [Note: technically speaking, Idris does not forbid writing non-terminating … $\endgroup$ Commented Sep 5, 2018 at 12:46
  • $\begingroup$ … programs, it just makes no guarantees about type-safety if you do. This is unlike other dependently-typed languages, where you are not allowed to write non-terminating programs, and thus often have to jump through hoops to get the compiler to realize that your programs is actually terminating in all cases.] $\endgroup$ Commented Sep 5, 2018 at 12:46
3
$\begingroup$

I have to thank you for helping me gain some insight on a very vexing question that I have always struggled with:

Why are there so many companies making similar products?

We see a big discrepancy in how things work: sometimes there is just one big player, which seems to have a "natural monopoly", something like Google, or Facebook. Not much point in having multiple Facebooks, right? So the also-rans don't run for long. This made me want to simply say that there should really be just one entrant in every field, and it should be the best. Put all your eggs in one basket, and make sure it is a really strong basket. Like, why so many car companies and models of cars? Just have one company, and a few models. Done. Instead, we have what we see: dozens of major car companies, all making oodles of very similar models and variations of products. Why on Earth do we do this?

A visit to the University library undid this idea, as I realized that people are creative and all want to make a mark on the world. Books, paintings, music I can understand. But cars? Who gives a rip? So I realized that there are things that are salient to people and things that are not, and this varies individually. I don't care about cars, but don't try to pry my favorite music or brand of coffee away from me. (But why don't people just choose the obviously best kind of coffee and music?) (Shh! you'll ruin the story)

Back to your question: people do not use functional programming because that is not how they think. If you go to a hardware store, you do not see motors and bearings, gear-sets and belts and reciprocators. You see angle grinders, drill presses, belt sanders, lathes... In other words, you see special purpose tools, not general purpose components. The home store has cabinets, washing machines and toilets, not wood, metal and ceramic. People want to get the thing that they are thinking of, not think about how to fabricate it. And they want tools that take them directly to that objective. Radio Shack stores are slowly closing, because few people have any interest in building electronic things that they can much more easily and cheaply buy. I don't know many other people who have built a shortwave receiver from random used parts in a box, or contacting someone thousands of miles away using a few watts of power and Morse code. And they are not in the least interested in doing so! Stunning.

I actually learned Lisp in college before C, but I have never used Lisp professionally. Most people are not going to construct a programming system from the ground up, they start with something high-level that is close to their desired outcome, and they snap parts together, or use high-level tools (like IDEs and visual design products) to achieve it. It is great that one can use the vi editor to create a spreadsheet (TeX) or a complex document (TROFF) or an AI program (by writing Lisp) or an operating system (by writing C), but Unix is still not a large share of the computing market (just in the areas where it matters) and even Apple is not dominant, because computing is mostly a matter of managing money (Windows and its products). It is a lot easier to write an ebook using Word than whatever the alternative is, which I never looked into because I learned Word decades ago. I learned it because it could do all the things I needed to, and it still does.

Some people would rather teach you how to build a watch than simply tell you what time it is, and it is true that if you teach a man to fish he can eat for a lifetime. But there are still a lot more people out there buying watches and going to restaurants, because they have other things on their minds than how to write complex computer programs. Even if they are programmers. So, "the industry" that you referred to, is really many industries, not just the academic view of "real computing".

So, thanks for prompting me to think this through in a general way so that I can write another very specific chapter in my next book, coalescing years of rumination, frustration and exasperation and hopefully creating some enlightenment out of it.

$\endgroup$
5
  • 4
    $\begingroup$ I see two errors here. People can think functionally just as they can procedurally. It just takes education and practice. And practice. It is a different way of thinking but accessible to all. Secondly, functional programmers in Lisp or Haskel (other choices...) don't, in fact, build everything from scratch. There are sophisticated libraries for functional languages just as for the others. CLOS for Common Lisp is one example. In fact, the ability to do meta programming in functional languages, just becoming possible in Java, permits very powerful constructs to be built and leveraged. $\endgroup$
    – Buffy
    Commented May 14, 2018 at 16:24
  • 1
    $\begingroup$ I have to agree with Buffy, the toolchains for FP are just as, if not more sophisticated, than the ones for imperative programming. In fact, because of the ease of, i.e. composing monads or using higher-kinded types, you often have to do a lot less of the grunt-work yourself in FP than in OOP. $\endgroup$ Commented May 14, 2018 at 16:29
  • $\begingroup$ See, for example: amazon.com/Art-Metaobject-Protocol-Gregor-Kiczales/dp/…. If you want to make someone very happy, buy the hardcover. Hmmm. I actually have one of those. $\endgroup$
    – Buffy
    Commented May 14, 2018 at 19:39
  • $\begingroup$ Also see various writings by Paul Graham. e.g. Hackers and Painters. Here is his blog: paulgraham.com/articles.html. $\endgroup$
    – Buffy
    Commented May 14, 2018 at 19:44
  • $\begingroup$ I don't think the reason FP isn't taught has anything to do with composability. It's just inertia. And maybe also related to the way incoming students already think. $\endgroup$
    – user20574
    Commented Aug 1, 2018 at 2:01
2
$\begingroup$

First of all, I question somewhat the premise of your query; there are many programs that do teach functional programming first.

That said, there are both historical and practical reasons for an emphasis on imperative programming. The Von Neumann architecture was modeled after the Turing machine, not after the lambda calculus. State change and mutability have been baked into electronic computers for as long as we have had electronic computers.

Our stateless languages are abstractions built atop the descendants of the Von Neumann architecture, not the other way, and the compiled code from Haskell and the like all utilize memory mutation. If we take it as a given that one of the goals of a CS program is to give a sense of how our computers work at a low level, functional programming does not advance this goal.

That's not to say that functional programming is not without value; I also teach it (as a second language), but curricular hours are not infinite, and I know from experience that all of the concepts that get jettisoned are each precious, irreplaceable rubies in their own right. When designing a four-year curriculum, you are faced with very hard choices.

It's also important to note (and this has has been discussed many times across this site) that mutability is not always easy for students to wrap their brain around, and often takes some serious time to sink in. For many students, this idea seems very natural, but there is always a significant group who gets stuck here. Not adressing mutability directly will make it very hard for a certain subset of students to pick up imperative languages on their own later. The idea that this can be hard is itself hard to understand for those who understood it naturally, but imagine that you are graduating students who can recurse, but get a little fuzzy when trying to follow

if(x == 7){
   x++;
}
if (x == 7) {
   x--;
}

Historically speaking, Fortran was more successful than LISP, and C was more successful than Scheme. Imperative languages resemble the machines they are built upon, and it's only natural, then, that schools in earlier days would have found it important to focus on Turing, Von Neumann, assembly, C, and the like.

Haskell is still trying to prove that it can compile efficiently. Meanwhile, it's only in the last ten years that lambdas have come to wide acceptance in industry. It makes sense, then, for both historical and continuity reasons, that schools have focused more on imperative languages.

$\endgroup$
5
  • 1
    $\begingroup$ With modern hardware, the way the system actually works is so different from traditional understanding, that I think it's actually counterproductive to think about it. From superscalar architectures to branch prediction and multi-core platforms, the way a CPU works internally is now very complex. I think it's better to let the computer engineers think about how the hardware will work and have computer scientists focus on writing well-designed, maintainable software, especially given the increasingly platform-independent nature of software development. $\endgroup$ Commented May 15, 2018 at 12:04
  • 1
    $\begingroup$ This blog by Raymond Chen (a C developer working on the Windows OS at Microsoft) is informative: blogs.msdn.microsoft.com/oldnewthing/20140613-00/?p=743. The branch predictor will completely change the expected execution of your code anyway, so best just not to try to anticipate the hardware behavior and focus on the quality of the software. $\endgroup$ Commented May 15, 2018 at 12:13
  • 1
    $\begingroup$ @AaronM.Eshbach You mean the Software Engineers? Computer Scientists shouldn't be particularly focused on writing software well-designed, maintainable software unless that is their area of focus/research. Computer Scientists should be focused pretty squarely on understanding complex interactions, algorithms, and computation. Branch prediction and superscalar architectures are great areas of modern interplay between CS and engineering. $\endgroup$
    – Ben I.
    Commented May 15, 2018 at 13:09
  • 1
    $\begingroup$ Perhaps Software Engineering would be the more appropriate discipline. There wasn't really a Software Engineering major when I attended university, so I don't know what that covers. At the school I attended, Computer Engineers took a blend of hardware and software courses (including Digital Design and working with FPGAs, etc.) while Computer Scientists took almost entirely software courses, with some basic education on K-maps and the like. $\endgroup$ Commented May 15, 2018 at 13:13
  • $\begingroup$ @AaronM.Eshbach Yeah, my major was not dissimilar. But things seem to be improving on this front. More schools are offering legitimate SE degrees, which really has blossomed into its own distinct field. I'm hopeful that this shift should be freeing CS majors to spend more time engaging in theory. $\endgroup$
    – Ben I.
    Commented May 15, 2018 at 13:19
1
$\begingroup$

The computing ecosystem keeps making smaller and smaller things. Imperative programming is far better matched to Arduinos and other small cheap computers that a k12 and earlier student is likely to tinker with (as opposed to just be a user of).

$\endgroup$
5
  • $\begingroup$ Today's tiny Arduino is much larger than the big computer we had here when I was a student... even my old cellphone has more oomph. $\endgroup$
    – vonbrand
    Commented May 17, 2018 at 20:14
  • $\begingroup$ Might be an interesting project to replace Wiring with a Lisp/Scheme derivative. Anyone done that yet? Will it fit any Atmel AVR? $\endgroup$
    – hotpaw2
    Commented May 17, 2018 at 23:11
  • $\begingroup$ I've got Maxima (a largeish Common Lisp program) running on my cellphone. It is far from the largest application there... $\endgroup$
    – vonbrand
    Commented May 18, 2018 at 13:03
  • 1
    $\begingroup$ Contemporary mobile phones have more compute+memory than not-to-distant supercomputers (Cray YMP, et.al.). Whereas an Arduino chip might have only 2k of RAM. $\endgroup$
    – hotpaw2
    Commented May 18, 2018 at 14:59
  • 1
    $\begingroup$ Added: I would not be surprised if a lack of appreciation for the disadvantages of functional programming might be a significant contribution to increasing global CO2 emissions, due to inefficient use of vast amounts of data center hardware. $\endgroup$
    – hotpaw2
    Commented May 3, 2019 at 21:52
1
$\begingroup$

It is just that we think procedurally. We "go to the store", we "cook dinner", we "do the dishes", and so on. The functional, recursive, based on induction, mathematical, thinking is very far from natural (ask any teacher of mathematics to students in first-year calculus courses, take a peek at what the colleagues of the math department think about their freshmen). Sure, programming is (thinly disguised) math, and so math style (functional programming) is more "natural" if you have groked math. Most people never get it.

$\endgroup$
4
  • $\begingroup$ I'm not sure your examples illustrate a difference between functional and imperative programming. Your verb-phrases would all be function names, the only difference is that in FP, instead of mutating the state of "my location", "dinner", and "the dishes", the functions would return the "my new location", "the cooked dinner", and "the washed dishes". If you wanted to do those things in order and pass the groceries we bought at the store into "cook dinner" and the dishes from dinner into "do the dishes", we just use composition. $\endgroup$ Commented May 18, 2018 at 12:38
  • 1
    $\begingroup$ @AaronM.Eshbach, you can model it that way, but it is a world away from how you naturally think about it. "I have a bunch of functions that input a whole world and spit out another" isn't the way we think, we think of changing an tiny corner of a (mostly unchanging, totally irrelevant) world, one step at a time. $\endgroup$
    – vonbrand
    Commented May 18, 2018 at 13:01
  • 1
    $\begingroup$ You could say that most of the world around us might as well be a vast collection of global constants and methods that we either are not aware of at all or at least not allowed to run. Finding something that actually responds is probably most of a baby's first 2 years of existence. So Procedural makes sense in that way. And, we don't just line up grocery shopping, cooking and washing dishes and push one button and walk away with a full belly and clean new kitchen, we have to push and pull every object and verify that every dish is clean. Not to mention biting the inside of your mouth (bug). $\endgroup$
    – Scott Rowe
    Commented Apr 24, 2019 at 11:23
  • 1
    $\begingroup$ It always seems not good to me when assertions are made about "we" as in all humans; evidence suggests that for any imaginable feature differing options exist. The correspondence between sequential thoughts and state transitions holds only at a very initial level and I believe it's the root of the difficulty so many students meet. Furthermore speaking simply from my point of view what you say is not true, I think functionally. In my very first moments with programming I was surprised that C did not behave like Haskell, which I didn't know: I would have appreciated to be shown it existed. $\endgroup$
    – user9137
    Commented Oct 6, 2019 at 19:06

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.