In retrospect, was burning bridges a mistake?

From the perspective of someone who teaches about 200 beginners every year, FTP has caused absolutely zero issues or consternation for students, and I would encourage people to step away from “won’t someone think of the beginners?” as a necessary failing of FTP.

There are plenty of things that students do stumble over in Haskell, and I have advocated on those elsewhere—but this isn’t one of them :slight_smile: (To pick an arbitrary example, the numeric hierarchy causes issues from day one! fromInteger vs fromIntegral vs realToFrac vs round vs …?!)

That said, YMMV, and it’s possible that alternative teaching methods do make this an uncomfortable discussion.

19 Likes

Separate thought, separate comment: I agree with earlier posters that traverse is massively underrated among beginners as a source of power, probably because (from their perspective) it is an unholy union of control flow management and data transformation. Despite being the same function, mapM (and forM) are easier to grok and using traverse as a generalisation is a very helpful pedagogic way of connecting the theory of typeclasses to real-world programming.

3 Likes

Thank you for reporting from the trenches. That’s … counter-intuitive. Do you teach deeply nested data structures with Prelude types mixed with custom types? Do you guide students away from holding everything inside Lists?

How do you explain the exception empty structure message; or how even a (allegedly) strongly typed language can produce such a run-time error for an out-of-the-box method applied to an out-of-the-box data item?

(I get it that the students you’re teaching today long after FTP happened won’t experience the churn that was the cause of major consternation at the time.)

Do you teach deeply nested data structures with Prelude types mixed with custom types?

A RoseTree a would qualify, presumably? In which case yes.

Do you guide students away from holding everything inside Lists?

On the contrary, we use lists all the time, but the first time they encounter fold[*] is when the Foldable type class is introduced, so we never discuss the possibility that a monomorphic version existed.

How do you explain the exception empty structure message; or how even a (allegedly) strongly typed language can produce such a run-time error for an out-of-the-box method applied to an out-of-the-box data item?

The notion of partial functions comes up early, since students need to get out of the mindset that functions can fail. read, head, minimum, etc serve as useful points of reference as historic failings in standard library design* that we know better than to introduce these days :slight_smile:

* (I contest that the main issue with such partial functions is naming, and that if they were called unsafeHead etc—and not part of Prelude, but some Prelude.Unsafe or similar—then they would basically be fine)

9 Likes

If something is known to be fundamentally broken, I would expect it to be fixed. Especially when we’re talking about a decade-old decision that is still echoing everywhere across the ecosystem (see both Foldable1 in base-4.18+ and the indexed-traversable package).

This as such belongs to the broader “when is base-5 coming” discussion, a seemingly gargantuan undertaking that both seems impossible (hasn’t been done in 15 years) and inevitable (gotta cleanup some day).

If one point of evidence is not enough, here is Graham Hutton:

GH : Yah, so I mean, I was one of the, I mean the Foldable-Traversable thing is something which I was not very keen on when it was being proposed and voted on. But actually, I mean, since, I was worried it would it would have a negative impact on how I teach Haskell, for example, but actually it didn’t. … But, yeah, I did worry about the Foldable-Traversable stuff, but in practice it’s had essentially zero impact on how I teach Haskell, which is good, because I did worry about that one.

14 Likes

If.

Pretty much every type class has its flaws and would had been designed differently if our past selves had all our current knowledge and experience gathered. Is Foldable somehow outstanding? I have not seen such evidence.

2 Likes

Making traversable and foldable available from the prelude was a big win for me, since I use those functions everywhere. Making traversable a superclass of foldable is something that I don’t consider a big deal either way – every instance of traversable should have been an instance of foldable anyway, and whenever this was not the case, I found it a rude surprise, but also just a minor papercut. Now, I don’t have to worry about that papercut anymore, which is nice, but not a big deal.

4 Likes

My experience is consistent with that of dixonary and Graham Hutton: the 200+ students we teach at Utrecht University every year also don’t really seem to have issues with it.

As for having methods such as any, all, lookupMin, lookupMax etc as members of Foldable: I actually find it very convenient. Even though many of these functions could be written as ‘f . toList’ or the like. I find that having to manually squeeze in the toList conversions everwhere is very distracting from the main idea of the algorithm. Hence, I’m happy that resolving to foldable I don’t need to. Furthermore, it is a nice bonus getting “the right” (more efficient) implementation for s.t. like lookupMin when it is available by default / without me having to worry about it again.

By the way @BurningWitness: in general you cannot implement TraversableWithIndex using just traversable + accumlator. That requires the index to be an instance of Enum. (and indeed, the default implementation form indexed-traversable only applies when the index is of type Int).

9 Likes

If you design your course carefully and you design your examples carefully, …

the tuple instance for these things is bizarre, even though there’s a rationale behind it.

Again, thank you for pointing to hard evidence. I still get the feeling from GH’s wider remarks GHC ‘could have done better’ and could have delivered an easier experience for learners. (Not every learner gets the opportunity to be taught by Graham.)

What’s also bizarre about ‘the’ tuple instance is why there’s an instance for a two-element tuple, but not 3,4,5,6,… Can someone explain the rationale behind that? (Yes of course I could declare them; but equally I could declare for a twople if that’s what I wanted – which I don’t.)

Yes sure. To be clear (for those who weren’t there at the time), a great deal of the FTP was welcome and sensible. Given how much Haskell tutorials are oriented around Lists, and that Lists had been treated specially (map :: (a -> b) -> [a] -> [b]; length :: [a] -> Int; foldr :: (a -> b -> a) -> a -> [b] -> a), suddenly dropping learners into higher-order typeclasses with all their abstruse error messages and “bizarre” instances seems … emm, poorly motivated. (map remained as dedicated to Lists, why not length? That length is applicable to ‘the’ tuple still surprises me; that it returns 1 is bizarre; that it’s not applicable to wider (I don’t say “longer”) tuples is a relief.)

How exactly does one convince (presumably) mathematically-aware students to “get out of the mindset” that e.g. the division function can fail?

Exactly, not to forget ++ as well.

This is very interesting to me! I would not have expected that. Helpful to have the experience shared here, thanks.

1 Like

That’s interesting because on the one hand, Haskell seems to be notoriously difficult to learn and on the other hand teachers seem to find it easy to teach …
Where are these hundreds of proficient Haskell students ?

(Alternatively they just don’t complain because they have no interest in Haskell whatsoever and just find someone to do their homework for them).

1 Like

They’ve learnt so fast (because taught so well) that they never need ask questions on StackOverflow nor reddit; they don’t participate in discourse because only strugglers hang out there; they’re busy working using Haskell in industry, and find GHC has exactly the functionality they need, so never need ask for enhancements. What enhancements come along, it turns out the Steering Committee knew exactly what they wanted by mind meld.

And in their coffee breaks they program (in Haskell of course) space lasers (in orbit on spaceship USS Dependent Nirvana) to shoot down all those pigs floating across the horizon.

Or … they found the Intro to Haskell/FP module something of an out-of-body experience; the programming lessons passed from the notes of the lecturer to the notes of the student to the course exam papers without troubling the minds of either; they got their grades; they moved on without a backward look. To the extent they reflected on it at all, they thought it rather weird for a so-much talked-about language to not have a records system. They presumed that must get taught in the second semester module.

1 Like

To the extent they reflected on it at all, they thought it rather weird for a so-much talked-about language to not have a records system. They presumed that must get taught in the second semester module.

So if you really want to impress us…port that wondrous records system to GHC, so we can all see what we’ve been missing for so long! Considering just how often you’ve promoted it, it should be absolutely awesome!

1 Like

Many of our students do indeed think (functional programming/programming in Haskell) is difficult. They learn many concepts/ideas (e.g. recursion, algebraic data types, higher-order functions, type inference, equational reasoning, typeclasses, some IO/monadic programming) that are new/different to them. There is simply a lot to learn, and hence they think this is difficult. I can sympathize with that. (By the way: I never claimed FP/haskell was easy to teach: since there is a lot to learn, so there is a lot to cover in a relatively short time; that sometimes makes it challenging).

However, I don’t particularly see why they should find the notion of a Foldable typeclass so strange/difficult. If you can accept that there is a “Num” typeclass that expresses that there are types that look like numbers (and thus provides functions to work with such things) then is it really all that surprising that there is a “Foldable” typeclass that models “container-like types” with functions that allow you to combine/flatten/reduce the container into some value? The hard part is actually explaining the idea of a fold (i.e. a higher order function), not so much that you can define something like fold/foldr/foldMap for Lists as well as some other data structure.

Of course there are some weird instances for Foldable. We don’t explicitly spend time on those, and in all likelyhood our students will not try to use any of those instances themselves throughout the course (either since they don’t need them to to solve their homework questions, or since they don’t fully realize that they could have (attempted) to use these instances). Whether they will hit those issues later I don’t know, but at least I don’t think it is a “beginner” issue.

In all honesty; issues in terms of tooling (e.g. getting a working compiler, editor etc) are much more challenging to deal with as a teacher: if students have an frustrating start it is difficult to motivate them afterwards. Moreover, it is time consuming to help students to get something to work, since every time it is a different puzzle you have to solve to figure out what is wrong. Luckily things have improved quite a bit in the last few years, and I hope they will continue to do so in the future :).

11 Likes

Fair enough. Having said that I don’t think FTP is bad because of beginners, but just because it makes my life more difficult ;-).

1 Like

A precis of our lecture on the topic:

Mathematical division (over, say, the reals) is not a function, because it violates the property that every element of the domain (say, pairs of reals) maps to exactly one unambiguous value in the codomain.

To make division into a function, we can either exclude (x,0) from the domain, or make the codomain a union of the reals with a singleton of some sentinel value indicating failure (e.g. { ⊥ } ). The corresponding pattern in Haskell would be either to restrict the input (via a newtype) or add a sentinel value to the return value. The latter is more common, and Maybe is the standard vehicle.


I’m sure you know all of the above! But this is more or less the exact formulation that is used to explain it to students who have (at that point) less than 3 weeks of functional programming knowledge, and they are able to take this in their stride as well. This is off topic though, so I’d recommend we don’t go on about it :slight_smile:

2 Likes