Future of Coding - Teaching Abstraction: Brent Yorgey
Episode Date: May 17, 2018Brent Yorgey is a professor of math and CS at Hendrix College. He studys functional programming in Haskell, type systems, and category theory, and more. He is the creator of the diagrams vec...tor graphics Haskell library. He taught Introduction to Haskell and The Art of Recursion at the University of Pennslyvaia (which were my two favorite classes in college!). In this conversation, we talk about Brent’s Monad Tutorial Fallacy essay, type systems, FRP, essential vs accidental complexity in Haskell, and the perils of reading academic CS papers and ways to overcome them. http://futureofcoding.org/episodes/23Support us on Patreon: https://www.patreon.com/futureofcodingSee omnystudio.com/listener for privacy information.
Transcript
Discussion (0)
Hello, and welcome to the Future of Coding. My name is Steve Krause. Today I have Brent Yorkey on the podcast.
Brent is a professor of math and computer science at Hendricks College. He studies functional programming, mostly in Haskell, type systems, and category theory and more.
He's the Art of
Recursion at the University of Pennsylvania which are both classes I
took and are two of the best classes I've ever taken in my life.
Brent is beloved by his students. He really inspires kind of a cult following
among those who have taken his classes and also those who read his blogs or check him
out on the internet.
I find that he really just really cares about the student experience and the way students
approach new and difficult but also beautiful mathematical functional programming discrete
mathy ideas.
And he really cares about making that experience
pleasurable, and so he thinks about how to structure and scaffold those concepts,
and I think he does an amazing job. So if you have a chance, I'd recommend taking a look at
the content that he's produced on his blogs and elsewhere, and the slides for his courses.
I'd highly recommend taking a look at those
if you're interested in learning more about functional programming,
discrete math, programming languages.
He's a master teacher.
And in this conversation,
we talk about his famous Mona tutorial fallacy essay,
type systems, functional reactive programming a bit,
the distinction between essential versus accidental complexity in Haskell. And at the end, we talked
about the perils of reading academic computer science papers and ways to overcome those
difficulties. And there was one question that I wanted to ask Brent, but I forgot to ask him
towards the end of the podcast. So I remembered and shot him an email later, and he responded in text.
And so if you stay tuned to the end of this episode, you'll hear a bonus question that
I asked about incidental versus essential complexity, and I read my question, and I
also read his answer.
So without any further ado, I bring you Brent Yorkey.
Hi, Brent. Welcome to the podcast.
Hi, Steve. Thanks for having me.
Yeah, thanks so much for coming on.
One of my favorite articles you've written,
and I imagine it's one of your more popular ones,
is the Monad Tutorial Fallacy.
Okay, right.
Maybe you could summarize it a bit for people who haven't read it.
Right. You know, so in, in Haskell, there's these things called monads, which is a particular
abstraction. And there was, this was probably like 10 years ago, I wrote that article or something,
but it still is the case that there's all these tutorials about monads and lots of people
start learning Haskell and they want to know what monads are.
And, you know, you have to build up a store of examples really to be able to get there.
And so people try to kind of learn about what they are and get some kind of, like, look at the abstraction before they really have the need for it.
And it doesn't work.
And so the article is just about how, you know, as humans,
really the way that we learn things is we see concrete examples,
and then we are able to sort of abstract out some patterns,
like see patterns, make connections, and start to see sort of bigger ideas.
But you can't start with the big ideas because if you have nothing concrete to ground them in,
maybe there are a few people who can do that, but I'm not one of them.
And most students that I've encountered are not. And so, so I started to do that in my teaching,
right? That if I'm going to teach about some concept,
I try to build up examples of it first
or sort of build up examples of why we want this.
Even in cases where I kind of introduce the abstraction up front,
I quickly followed that up with,
okay, that probably made no sense,
so let's look at a lot of examples.
And then we'll come back and restate the abstraction
and then see how it fits.
So, yeah, and I guess that was the article where I, I, I made this, uh, just kind of a passing joke about, you know, that someone,
someone said, Oh, I understand monads now they're like burritos. And it was just totally a joke.
Lots of people quote it now. And I'm not, I'm not sure that everyone realizes that it's,
it's a joke. I think some people think really monads are supposed to be like burritos but i don't know i i think i read
somewhere else on the internet uh that someone that someone was partly joking yeah this was
mark jason dominus uh wrote it on his blog this article where he's like actually monads are
burritos and he sort of he was he was but I think, uh, I'm not sure everyone
quite understands anyway. So yeah, he, he and I have actually eaten burritos together. So,
so that's, you know, that makes sense. So I think that, um, like the, the, the monads are like
burritos line, I think kind of gets at why it's such a tempting fallacy right
because yeah like once you get something the way it's stored in your brain is is you you store the
analogy like uh but that's right but you can't you can't transfer the analogy yeah to someone
else's brain without the lower level details exactly well and and part of the reason it's
a fallacy is that it plays into this thing called the curse of knowledge, which is this well-attested psychological phenomenon where it's very difficult for you to remember what it's like to not know the things that you currently know.
And you sort of assume that people will understand what you're trying to tell them because it makes perfect sense to you.
But it's very hard. And this is part of, I think, a big part of what being a good teacher is about
is training yourself to, you know, be able to get into that mindset of like, what was it like when
I didn't know this? What do I need to convey in order to help someone build up to the point where
they understand? And so, right, once you understand some idea like monads,
you sort of forget all the difficulty that you had,
all the details that you needed to get there.
And it's very tempting to just say, look, here's how I understand it.
And you just like lay out your high level idea.
And that's not enough for people to really get it.
So, yeah, so that is, yeah, that's what I said is I called the
monad tutorial fallacy. But obviously, it applies to lots of things, not just monad tutorials.
Yeah, of course. So on the subject of making, of taking the perspective of trying to
understand how hard things are, for beginners, I saw you have a new project
on making type errors more understandable.
Right.
Yeah, I don't know if I'd call it a project
that makes it sound, you know,
all official and stuff.
It's really just sort of some idle speculation
at this point.
I hope it goes somewhere.
But yeah, so this is something
I've been thinking about in the context of, um, I've been developing this language called disco,
um, which is intended to be sort of a, uh, a functional teaching language that you would use
in the context of a discrete math course.
And I could talk more about that if you want later. But, you know, since I'm designing this language from scratch, I have the opportunity to, you know, rethink, like, I don't have to do things
exactly the same as other languages do, just because that's the way it's always been done.
Get to rethink some things. And so one of the things I've been rethinking is, you know,
there's this such a tradition of if you have some compiler or type checker,
right, when it gives you errors, you basically get a big list of, you know,
a big bunch of text that says here are all the errors.
And, you know, type errors are notoriously scary,
especially for beginners.
And I think, you know, part of the problem is that they're so static.
And it's either you're going to have too much information
if it's just a beginner.
Maybe they don't need to see all the information.
They just want something simple that kind of tells them, you know, at least the beginning of an idea of
what the problem is. And even if you're an advanced user, I mean, how do you know which
information you should include in the error? Maybe there's some information you want to see and
other you don't. And so I've been thinking about, okay,
how do we go about building sort of interactive error explanations?
So it might display, say, okay, there is a problem here.
And basically the problem is that bool is not the same as int.
And you would say, well, okay, obviously bool is not the same as int. And you would say, well, okay, obviously bool is
not the same as int, but why did you think this should be an int? And then you could maybe
expand something. And there's different ways you could imagine the interaction happening, but
essentially you get to sort of interact with it and ask for the information that you want,
and you can kind of drill down and maybe even have it be dynamically generated as you're,
as you're exploring it.
And so at this point, I mean,
that's kind of a vague idea and I I've I've been,
I've been thinking more about just how do you go about generating interactive explanations in the first place?
And not so much in terms of what's the UI actually look like to interact with this.
And so, you know, it gets into some type theory and stuff, but I think I have some interesting ideas for how to, like a sort of a
principled way to approach
designing and generating these kinds of interactive explanations.
Yeah,
there was one phrase in particular that I found fascinating.
You called it an untyping derivation.
Yes, right.
Yeah, so that's the idea is that, so, you know, if you're thinking about type systems sort of on a formal level, a typing derivation really is a proof.
It's some explicit evidence that proves that a certain term has a certain type.
And you can think of it as kind of a tree that you could there's some kind of type error, then I think what you
really want is you actually want a proof. You want explicit evidence that says, why does it not have
a type? And you can think about designing a sort of an untyping system, an untyping derivation
that is sort of the type of these trees
that would constitute a proof
that a term didn't have a given type.
And then you could use those trees as the basis for,
you could interactively explore them
and that could kind of be used as the basis
for exploring these errors.
But, you know, and there's a lot of room. There's a lot of room
for designing different kinds of untyping derivations, but it at least gives you sort
of a theoretical framework for being able to say, you know, this is a good untyping derivation or
this isn't because it has to relate to typing derivations in a certain way. And you can even formally prove
that, you know, that it has the properties that you want. Yeah. So I think one of the things,
like an example of like a problem that would make type errors, like difficult to make better,
that I think you kind of hinted at in your paper.
The way I conceptualize it is,
if I'm a programmer hacking away at my Haskell code,
and I'm in a specific part of the code,
and I compile, and there's a type error,
oftentimes what makes the type error so confusing is that the the error is the compiler complains about something that's like not
near where i'm editing it's like complaining about right what or like another way to phrase
it is that like it kind of has it backwards like i backwards uh from the perspective of the way
that i'm thinking about the problem so like right, right. Right. I, I want something to be like an int,
but like elsewhere I like, I,
another function expects a bool, but right now I think I understand what you're
saying. Right. That you know, at some point there's a, there's a mismatch,
but you know, which side is to blame?
Yes, exactly.
It really is an int, but you want it to be
bool and that's what's wrong, or no, it really should
be bool and something else made it an int and that's
what's wrong. Yes, exactly.
Because the type error is just an inconsistency.
From the perspective of the programmer,
it's
like something's wrong.
That's right. Yeah, yeah.
No, and I think this is something that interactive error explanations
could really help with.
Maybe, I can lecture.
Because if it's just a static message,
well, then that message has to make
a lot of choices and assumptions
about what's wrong.
And it might get it wrong.
That might not be the problem. But if you can explore it interactively, you know, it's just like, these things don't match. Now you get to explore why.
And, and now you get to, you have a choice and you says, okay, this thing was a bull. This is
an int. And you're like, well, yes, this thing should be an int. Therefore the problem must be
over here. And you start exploring on that side, Or if it was the other way around, then you start exploring the other way.
But you actually get to bring your intentions into it and then use your intentions to help
guide which parts of the area you explore to drill down to the thing that actually is wrong. It's making me wonder if there'd be a way for you to somehow give the compiler the extra
information that...
Just before compiling, the user's cursor was over here and the compiler was like, oh, potentially
that means they're looking at the problem in this direction.
Maybe that's,
that's expecting too much.
Yeah.
I think,
uh,
I don't know,
intuitively to me,
it seems like you wouldn't,
that would,
that would do the wrong thing.
I mean,
because,
you know,
just because you,
you've just been editing something that doesn't necessarily mean that the
error is just in the part that you just wrote,
right?
It could be that there was an error in something else you wrote,
but maybe,
you know,
especially if type inference is involved,
it could be that it was inferring some type for something,
but just not the one you thought or the one you wanted.
And now the error is showing up here because it's finally,
you know,
manifesting here,
but I don't know.
Yeah.
Of course. You can maybe imagine trying to apply some machine learning to it, you know manifesting here but i don't know yeah of course you can maybe imagine trying to apply
some machine learning to it you know yeah we could gather a bunch of data about how people
explore these things and what they were doing beforehand and then maybe we could um you know
make better guesses but yeah i don't you know ultimately i think anything that tries to be
too magic is just going to end up being wrong a bunch of the time.
It's just going to be frustrating.
Yeah.
So the goal here is to kind of give the programmer control so that they can they know exactly what's going on and they can they can guide the process using their what they know their intentions to be. So you came across these ideas while developing this disco language.
I'd be curious to hear more about the motivation for that.
Yeah, I'd be happy to talk about it.
And it's very much at a prototype stage now.
I may end up using it in teaching a discrete math course like in a couple of years.
But sort of the motivation is, you know, functional programming is great and we want students to be exposed to it.
But sort of where do you put it in the curriculum?
Right now at Hendrix, I teach a functional programming course, but it's an upper level elective course.
It's mostly juniors and seniors taking it, which there's nothing wrong with that.
But, you know, it would be great if students could be exposed to functional programming ideas much earlier, and then they could use those ideas and sort of use them as a
lens to look at all the other stuff that they're learning in the curriculum. And I think it would,
you know, have more of an impact on the way that they think about things.
But then the problem is, okay, well, so do you introduce another, you know, beginning functional programming course that
you put early in their curriculum? Well, we can't add another course because we just don't have
anyone to staff it and we can't add more courses to our major. Do you make your introductory
programming course about functional programming at a small school like this? We can't do that
either because we only have one introductory programming course and it's really serving
a bunch of different populations, not just potential computer science majors, but also, you know, like physics and
chemistry students who, who need to, who want to know some programming to, they can, so they can
apply it to their field or just like, you know, creative writing majors who just think programming
sounds interesting. And you know, introducing functional programming in an introductory course would make sense if it was all potential computer science majors.
But like, especially for those science, other science majors, um, you want them to learn something that they can go back and, you know, directly apply.
And, and the fact is that not many people are using Haskell in, uh, you know, doing physics simulations. And so, I mean, maybe they should, but
it seems more useful to teach them like, you know, Python or something like that,
which is what we actually use in our intro course. But so the idea is, and we're not,
I'm not the first to have this idea, but it actually goes really well in a discrete math
course. So typically discrete math would be required for a computer science major.
Um,
and functional programming fits that really well.
Um,
the,
the topics just,
um,
go together really well.
And so,
and other people have done this,
have,
have,
have used,
uh,
you know,
functional programming in a discrete math course,
but typically they use some existing general purpose,
functional programming language like Haskell or OCaml or something like that.
And the problem is, you know, you're not, you don't have a lot of time.
It's not a whole course where you're going to go into the details of Haskell.
Some of these students haven't done a lot of programming before.
And you don't want to spend a lot of time kind of having to carefully scaffold things and tiptoe
around the features of the language that you don't want them to learn about, or you don't want them
to get confused by or whatever. And just sometimes there's impotence mismatches with the kinds of
things you want to do in a discrete math course. So, so to me,
it seemed like a fun opportunity to design a language specifically that would
be designed for teaching.
It would be designed so that you could do discrete math stuff in it very
easily and kind of give it a platform where you could teach the basics of
functional programming and give students a fun playground to kind of give it a platform where you could teach the basics of functional programming and give students a fun playground to kind of learn about the
discrete math concepts that you're teaching them.
So that's kind of the genesis of the language. You know,
I guess I could talk more about some of the specifics of it if you want,
but that's sort of where I'm coming from.
What I'm trying to accomplish.
Yeah, that makes sense. I, yeah,
I'd be curious to hear about some of the exciting features
that make it different from Haskell or other languages.
Sure.
Some things are just syntactic,
where I've tried to make the syntax as close as possible
to standard mathematical practice,
as opposed to necessarily making it similar to other existing functional languages. And partly that's so that students have
less of a, you know, cognitive dissonance going back and forth between their, you know, math
textbook and the programming language. Partly, honestly, it's also to make it more attractive to math faculty
who might want to pick it up and learn it in order to use it in a discrete math class.
So actually at Hendrix right now,
the discrete math course is required for the computer science major,
but it's actually taught by math faculty.
And so if I wanted to do this long-term,
I'd have to get some buy-in from them.
Although it looks like I may have an opportunity
to teach it myself in a few years, possibly.
So some of it is just syntactic.
So for example, you can multiply things
by putting them next to each other in many situations.
And it sort of syntactically distinguishes between multiplication and function application,
just depending on like if the thing on the left hand side is a variable, then that's
function application.
But if it's like a numeric constant or something in parentheses, then it treats it as multiplication.
You know, that's kind of a silly thing, but it makes a difference.
And students, you know, I've seen students do this a lot.
When they're learning Python, they try to put things next to each other to multiply them,
and it's like they get some really weird error.
Other things like...
I guess it's only a syntactic thing I can think of off the top of my head,
but other more foundational things like there's no floating point numbers
because we don't need those for discrete math and they just make everything
more complicated.
So there's only there's natural numbers and integers and rational numbers in
terms of numeric values.
And actually there's also like numbers mod whatever,
like the number integers mod 7.
That's built in as well.
And there's also subtyping.
So if you have a natural number,
but it's a function,
what's an integer,
you can just pass it.
You don't need a conversion function,
which makes the type system more complicated.
But I think otherwise,
you know,
just in mathematics,
no one thinks in terms of,
Oh,
I have a natural number.
I need to convert it to an integer.
It just isn't integer.
Right.
Other things.
So there's going to be, and this part is not really implemented yet, but I think I have a pretty good idea of how it's going to work. In addition to having lists built in, there's going
to be, you know, sets and multi-sets and maybe even, and like binary trees and basically you know how do you if you have a say
a set or a multi-set or something and you want to do something with it like you know add up all the
elements how do you do that and I think what what's gonna what's gonna happen there's basically
like a like a fold operator.
You give it some binary function and you say,
fold this set using this binary function.
But it has to check that the function
sort of has the right properties to do that.
So for example,
if you have a set,
the function had better be commutative
because you don't want to be able to observe
the specific order in which the elements
are actually stored, right? You should get the same answer no matter what order they're in,
because a set doesn't have an inherent order. So then how do you check that a function is
commutative? Basically you can't, you know, it's not really decidable just to look at the code of
it and, and, and check. But basically you can put a little annotation that says this function is commutative,
and then it'll basically quick check it, right?
It'll try a bunch of random inputs and make sure that it works, right?
And probably there's going to be a lot of built-in functions in the standard library
that it's going to know O plus is commutative.
So that's just kind of built in.
It knows that.
And that's just one example of a kind of property you could specify on a function that it'll check for you.
Like you can say, oh, this function is the inverse of this other one.
Or you can say this is idempotent or associative.
That's cool.
All that's going to kind of be built in.
Not exactly.
Well, no, I guess it is built into the type system.
So like idempotent or associative will be a type,
or, like, you said it's more like an annotation.
It's more like a constraint.
I think it's kind of like a type class in Haskell,
except there's not really any methods associated with it.
It's just kind of like a, there's like a proof associated with it.
So, yeah, the interesting design challenge that I'm going to have to, I'm going to really tackle this summer, you know, the language. So I think the things that I've mentioned so far,
it's got these constraints, it's got subtyping and it has to have polymorphism or else it's
going to be really stupid to teach functional programming and say, well, we have to write a separate map for every kind of list you would want to map
over. That's stupid.
But it turns out the combination of those three things is,
is really non-trivial. And I've been, I think it's sort of because of I think,
I think it's sort of just on this side of, of tractable because of some
simplifying assumptions
that that i make about um you know how the subtyping works or what what what what things
are decidable etc um but uh yeah it's so it's it's really been fun kind of diving into um
you know some pretty complicated uh type system stuff
and figuring out how to implement some complicated algorithms to do the type checking.
But ideally in a way that will sort of be
that will make the language feel natural to students.
And I think it's sort of
it's also partly an experiment to see how well that works, because you always worry that the more complicated you make something, even if you're doing it in order to kind of make some nice looking API that the abstraction is going to leak.
And students are going to suddenly see all this scary type stuff and you're like, what is going on. So that'll be the challenge is, is can we make all this, all these features work together,
like work really hard under the hood to make it all work seamlessly so that students don't
necessarily have to know all that complex stuff that's going on, but that they can use the
language and it'll, it'll feel natural to them. So we'll see. Yeah. That's interesting that you described that trade-off.
Do you think that it's easier if you were to make...
If you were to have less features in the language,
have the API be simpler?
The under the hood could be simpler too?
Yeah, it could, right.
So, I mean, for example, if we gave up subtyping,
so you say, okay, no, like every type is a distinct type.
You can't use a natural number as an integer.
There's a conversion function you have to call explicitly.
That would make the type system a lot simpler um quite a bit
simpler um and well it would make type checking a lot simpler um but it sort of flies in the face
of standard mathematical practice and i think students and you know math teachers would find
it weird that you kind of can't just use a natural like like a natural number is an integer What do you mean? I can't just use this like I've got a
Verdict what it what can there's no conversion happening. It's just the same value, right?
So
You know, but the point is like basically those conversion functions are
Explicit hints to the type checking algorithm that you're switching from one type to another and if you don't have that well
Then it's got to do a lot of hard work to figure out
where to insert those itself, basically.
So yeah, there's always trade-offs, right?
And there are definitely other things we could do to make the type system simpler at the
expense of making the language more awkward or not having the many features or something.
Interesting.
This kind of reminds me of something I heard you talking about,
about your diagrams library in your Haskell diagrams library.
And I was curious to get like maybe more of a story on.
You mentioned how in a lot of libraries, they, people often combine the notion of like a vector and a point. Um, but, but you like run into trouble if you do
that and it's better to treat them as two separate things. I'd be curious to hear why,
why is that such a bad thing to do? It seems innocuous. Right. Well, yeah, I think the,
the bigger principle at stake is that, you know,
the way something is represented is not the same as what it is. And,
you know, it's, it's so tempting to represent vectors and points, you know,
in exactly the same, well, they are, they are presented in the same way, right?
They're like, let's say, in two dimensions, right?
It's just a pair of numbers.
And so because they're represented in the same way,
you think, oh, they're basically the same thing.
But they're not.
So an example is it makes sense to take a point and add a vector to it and you get another point.
It makes sense to subtract two points, quote, quote, subtract two points, and you get the vector, you know, pointing from one to the other.
But it doesn't make sense to add two points. If I literally have some location in space and another location in space,
adding them isn't really a sensible thing to do. And if you do that, it probably means you have a
bug in your program. And so if they're actually different types, well then, you know, ideally,
you can design things so that if you try to add two points, it's going to the type system is going to yell at you.
Right. Another example of the difference is that if you translate a point, you get a different point.
Right. You move it somewhere. If you translate a vector, right, vectors don't have a location.
They're just sort of a magnitude and a direction.
If you imagine a vector as a little arrow sitting somewhere on the plane, well, if you move the arrow
somewhere else, it's still pointing the same direction and it's still the same length. You
just moved it. That doesn't change its identity as a vector. That's another example of how they're
different. And honestly, the way I came to realize this
was because in an early version of diagrams,
they were the same,
and I actually had an honest to goodness bug
that took me a long time to figure out, to track down,
because of exactly this issue
that I was applying a translation,
and it was actually changing some vectors
in a way that it shouldn't have.
And as soon as I realized what was going on, like the light bulb went off.
And maybe before I had seen this idea of vectors and points being different. So, I mean, the sort of fancy term for it is an affine space.
So that's the sort of algebraic structure that these form.
So then I kind of redesigned it so that they were actually different types.
Now, I mean, in Haskell, actually, a point is actually just a new type wrapper around a vector.
So literally, they are represented as the same thing at runtime, but they're different.
They're different types.
So yeah, this idea of being very specific about your types
as like a safeguard, I find it a very tempting idea.
But then in practice, sometimes I find that it feels more frustrating
than it's worth.
One idea I've had that's kind of related is,
and this might just be taking it either an extreme or maybe, anyways, it's kind of a wacky idea.
But one thought is Booleans seem like very generic concepts. And often you don't actually
mean true or false. Oftentimes you just mean like like, what you're talking about is, like, equal or not equal.
And so it got me wondering if we should get rid of Booleans and instead have more specific types, like equal, not equal, less than, greater than.
And, yeah, that's, or, like like or even with numbers like you know integers maybe
are too generic like we should um like in physics how all the numbers have units and it becomes like
easier to know what you can divide and multiply you know so but so on the one hand it's like
that sounds really pretty and it could kind of give you like bowling lane bumpers uh and make sure
that you don't do anything that doesn't make any sense um but on the other hand uh i feel like
in practice that would it would be um cumbersome yeah so um so your idea about booleans is right
on in a sense um so there's a great um there's a great blog post by Bob Harper wrote, um, called
Boolean blindness.
Um, I highly recommend any programmer to read this article.
Um, and he talks about exactly this, this problem with Booleans that once you have a
Boolean, um, it's just a single bit and it doesn't tell you anything about where it came
from or what it means.
Right.
And you have to,
you as a programmer have to remember what it's supposed to mean.
And you sort of have to kind of recover that information.
And so,
excuse me.
It's this idea that typically,
so I don't think it would work to sort of list out in advance
all the different things you could want instead of a Boolean
because it's going to be specific to each situation, right?
Let me give a specific example.
So like if you...
You know, if you want to know, I don't know, this isn't a great example.
It's the first thing I can think of.
If you want to know if a list is empty, right, you shouldn't just return a Boolean.
Instead, you want to return, essentially, you want to return a proof, like a constructive proof of whether it's empty or not.
So maybe you would make a data type.
So I'm thinking in terms of like algebraic data types.
If you make a data type, it has one constructor called empty and another called not empty that has, you know, that has to contain a value.
And so if the list is empty, you return empty.
And if it's not empty, you return not empty
and the first value in the list.
And that way, you know, for example, you know,
and it kind of depends on what you're going to use that Boolean for.
So if you had some code that said,
oh, I'm going to check if the list is empty.
And then if it's not empty,
I'm going to project out the head of the list and do something with it. Right. But if you
look at that code, you're like, well, how do I know that projecting out the head of the list is
safe? Well, it's because I just called this function that told me and it gave me false.
So I know it's not empty. So I know it has to have a first thing. But you have to do all that reasoning in your head, right?
And if it instead returned this data type that said either it's empty or it's not empty,
and here's a proof, here's the first element of the list,
well, now you just pattern match on that, and you've got your element,
and that code is manifestly safe, right?
You can look at that code and say, this will never crash,
and I don't have to do any kind of sophisticated reasoning to figure out why. It sounds like it's just pattern matching.
Yeah. I mean, ultimately it's not actually that complicated. It's just this idea of
often if you have a function that's returning a Boolean, you should really think about, you know, maybe you want something more informative than a Boolean, right?
What information would you actually want to kind of prove that the answer is true or false?
And you should return that information instead.
Interesting.
But it's kind of ad hoc, right?
You can't design up front a list of replacements for Boolean. In each specific situation, you have to decide what information am I using to decide whether it should be true or false?
Well, I should just return.
I should make a data type for that information.
We could take an example, like the quality operator in Haskell returned true or false.
So I guess what do you think would make sense to redesign it to return, uh, like greater than or less than or equal or how, how would you redesign that function?
Oh, right. Okay. Well, yeah. Um, so that's a, that's a tricky example. I mean, I guess
you, you can really only do something better with that in a, like a dependently typed language where you can actually return a proof, literally, that two things are equal.
Which you can kind of do in Haskell if you play some tricks, but like using GADTs and stuff.
But yeah, I mean, I guess that's really it.
I mean, so it's not so much
you're returning an algebraic data type,
you're returning a proof, right?
A Boolean really is just asking,
is this, is something true or not?
And so instead of just returning a Boolean
that says yes or no, you say,
yes, it is true and here is a proof or no, you say, yes, it is true.
And here is a proof or no, it isn't true.
And here is a proof.
Because that's really the information that you want is like, why is it true?
Yes.
Okay.
So in the case of like equality, you would just say like they're not equal because five is bigger than three. Is that kind of,
yeah.
Um,
you,
well,
in the case of equality,
it depends on what you want to use it for.
Often in the case of checking for equality,
you,
you really only care about the case when they are equal.
If they're not equal,
you're just not going to do anything.
And so you don't really need any information other than the fact that they're not equal. Um, but if they are equal,
then now you you've learned something. You've learned that these two things are the same
and maybe something else you're going to do later is going to depend on that.
And so you really actually like a proof that they're the same, that you can then use
later so that you know, um, that what you're doing is safe.
I don't have a good example of that off the top of my head.
And this is not just theoretical.
I mean, you'll find libraries with functions of exactly this type in Haskell
and, you know, in other functional languages,
like dependently typed languages.
So people really do this.
I think maybe part of the point is that you can really only do this in a language
that has a sufficiently expressive type system that you can encode non-trivial proofs
about, you know, values in your program.
And, you know, so at a minimum, like you want some kind of algebraic data types values in your program.
So at a minimum, you want some kind of algebraic data types.
It'd be really, really unwieldy to do this in Java, for example,
because it doesn't even have some types.
But you don't necessarily need dependent types.
You can encode a lot of proofs that are just some kind of algebraic data type, but without dependency.
Cool. It's kind of like on the other side of things.
There's the creator of
the Clojure language, Rich Hickey, talks about
he kind of represents, in my mind, the other side of the argument.
He argues that you should kind of not use strong typing.
His argument is that we programmers love abstractions, but it's like fun to write them, but you often get them wrong and they're hard to explain to other people and you need to change them.
So he argues for like keeping just very simple very simple values just numbers booleans lists
so i'd be curious to hear because i because i know you're someone who's so excited about like
abstractions multiple levels of abstraction so how do you reconcile the idea that abstractions
are like difficult and um you get them wrong and they're hard to communicate right yeah well i mean
the reason abstractions are difficult and you get them wrong is because programming is hard.
Like it's not, it's not because abstractions are bad and they make it, they're hard to use.
It's that they're sort of, if you're, if you're only using, you know, Booleans and ints and strings,
you can kind of pretend that everything is fine.
Because, well, everything type checks because, well, this just expects an int and this expects a string. But they aren't fine, right? Your program is full of bugs. And so when you start
using, you start trying to build abstractions, you run into the fact that, A, there's more bugs that
are showing up actually as type errors, for example,
which can feel frustrating, but of course you're finding bugs. And B, you run into the fact that
you don't understand the domain well enough to be able to build the right abstraction. And
that can be sort of a humbling experience.
But again, that's not really the fault of the abstraction.
And so, you know, but by sort of pushing onward and, you know, iterating and trying to design good abstractions and sort of, you know, good types to model your domain, a good API that models the kinds of operations you want to be able to do, that everything is sort of consistent and elegant.
That takes a lot of hard work.
But when you do that, that work is work to understand your problem domain.
And you're going to have to do that work sooner or later, whether you do it up front or whether you do it down the road
when you have tons of bugs and you can't understand what's going on,
or you can't maintain it because something needs to change
and just the way you've designed it just doesn't support that
because you didn't think through the domain well enough.
I was going to say that this, to me, feels kind of like it's the opposite
of what you argue in the monotutorial fallacy.
Or not the opposite of what you argue, but it's like for humans,
it's very easy to go from like simple
things to more abstract things but sometimes in haskell or like statically type language it feels
like you need to have a really good abstract understanding of the domain before you can
get started uh yeah okay i see what you're saying. Right. Um, you know,
I guess it's right. It's not to say that. Um, so I,
I would never argue that, you know,
you're not really a programmer unless you can use all these crazy
abstractions. Right. Um, not at all. Um, but I would say, you know, to argue that we shouldn't
use these things, you know, yeah, it does, it takes a lot of experience to be able to get to
the point where you can use them well. But, but that's, it's time well spent, and it's effort
well spent. Because when you get to that point, you're able to work at such a higher level
that it really pays off in terms of your productivity
and the kinds of problems you can solve.
So when you're building a new application in Haskell
and you don't entirely understand the problem domain,
is there a way to start concrete and then
build up uh like how you would in a kind of dynamic language where you kind of just messily
pass around strings and ends and not check you know to make sure everything is hunky-dory yeah
and then over time sure you can um you know and i i usually fall somewhere in the middle where i
kind of you know just because of my experience there fall somewhere in the middle where I kind of, you know, just
because of my experience, there's certain things when I know, oh, I kind of, I know
what pattern this is going to fall into.
I know how I want to, you know, build an abstraction for this instead of just passing around strings
or whatever.
But, um, but you know, you also, you just try things out and you kind of write some
code and maybe you'd end up, maybe end up, I mean, I've had the experience of writing much code and then suddenly realizing, oh, I know the right way to do this.
And I just throw it away and start over.
But, you know, the other thing about having a strong static type system is that it makes it really a joy to refactor.
I've had the experience of, you know, spending a day refactoring some Haskell code.
And then as soon as I get it to compile, it works.
Um, and that doesn't happen all the time, but, um, it is really, you know, fun saying,
all right, I need to, I need, I realize now this shouldn't be a string.
This should be something else, or I should, I should organize this in a different way.
So I just change it.
And then I just, you know, I just start going down the list
of type errors, and I fix each one. And when I'm done, you know, I'm done refactoring. So it
actually, I think that kind of exploratory programming where you don't quite understand
the domain, and you're just trying things out with some simple abstractions, then sort of,
you know, building it up as you go.
I think that works even better in a language with, well, not so much about what abstractions
the language has, but just about having a strong static type system. Because it sort of gives you
that safety net. If I had a giant, you know, a giant Perl program and I wanted to refactor it, I mean,
I would, that would be, that would be awful, right? I wouldn't, I would just do something
else because if I started making some giant refactor, I'm not at all confident that I'm
going to be able to get everything right, make all the changes correctly that i need to um so yeah yeah
yeah i i have a a big javascript program that i've been been been accreting over the last few years
and it's it's a bit of a mess now and i i don't really want to look at it right exactly yeah
yeah and like yeah it's like i don't want to touch it because as soon as i make some changes it's all
going to fall over and i just can't i'm not not going to be able to, to fix it. Right.
Yeah, it definitely works now. So if I just, don't sneeze, it'll fall over. Let's see. I had a few questions. A few more questions.
Let's see.
One series of questions I had,
but I'm not sure how they're going to go. Well, just try them.
So, Ian, are you familiar at all with the JavaScript React library?
I am not.
Is that something with, like, functionally reactive programming stuff?
Yes, yes.
That's why I'm bringing it up.
Yeah, I've heard of it, but I don't know.
I'm not familiar with it.
Well, so anyways, I'll kind of come at this question a different way.
Okay.
So when you think about abstraction, a lot of time you think about hiding details.
Right. think about abstraction a lot of time you think about hiding details right um and what's great
about hiding details um is reuse and composability you can like build a thing yeah and then and then
use it in a bunch of different places right um without having you know without having like
re-hook it up somewhere right right yep um but then uh the the stinky thing about having like hidden details is that you can't
um get at the i guess the the thing that's annoying is like um hidden mutable state
uh like local mutable state if you have some component that has like local mutable state
um that that's like leads to leads you you down this rabbit hole that you don't like.
Well, why do you need local state?
Why do you need local mutable state?
No, I'm joking. Go ahead.
Well, the idea is, let's say you have an input box on your website,
and you want the person to be able to type whatever they want,
and then when they're done, they hit submit.
And you can build it so that the whole application able to like type whatever they want and then when they're done they hit submit and um and you
can build it so that like the whole application knows about every single key press you type into
that that um input box right um but but now it's like the input box is kind of tied into the page
and it's hard to like abstract extract the input box and put it like in a different right right
um and so like place. One way people
solve it is with
cursors. You create an input
box and it stayed.
Anyways,
I don't know. I'm kind of getting
lost in my own question.
Maybe you're not...
Maybe
it's not going to make sense because I'm
coming from a React perspective.
In Haskell, this isn't a problem.
Everyone just uses, like, state is kind of represented.
Global, like there's no, yeah.
No, I mean, I think it can still be a problem.
So I've only done a little bit of functional reactive programming.
And I mean, so I was using Reflex in Haskell.
And I don't know, even, I mean, you do end up using mutable state there as well. You know, often like the way to do it
is you set up this like an IO ref,
which is like a mutable reference cell
to store something.
And then there's various things that interact with it.
I think, you know, as a species,
I think we just still haven't figured out
how to do a UI programming.
I just, and I think, you know,
I think functional reactive programming has a lot of great ideas.
And I think it's headed very much in the right direction,
but I still think we're just, we're not there. And I don't know.
I can't put my finger on exactly what makes it so difficult.
But just as an example, I was working with a student.
We were making this user interface using an FRP library. And, you know, it turns out that like, it's very easy to get
into situations where you, you write things that either like depend on things in the future in a
weird way that like just makes your whole program hang, or just things aren't computing exactly what
you think they should be. And then it was very, difficult to debug because um there's just kind of it's hard
to figure out how to to instrument it to get the information you want out of it um so it was
definitely not you know it wasn't it wasn't the kind of smooth programming experience that um
that you that you would like so i don't know i don don't, I mean, I don't have a lot of answers, but it does seem to be a very difficult,
very difficult area.
Yeah. Would you recommend like to learn about FRP kind of stuff,
like going back and reading like Colonel Elliott's papers or playing more just
like playing around with a library like Reflex?
Yeah, I think I would, I would start by just playing around with a library like Reflex? Yeah, I think I would start by just playing around with a library like Reflex.
You know, I think once you've maybe seen one or two libraries,
it's interesting to go back and read some of the foundational work in the field.
But I've read some of those papers.
But I don't know. It's, it's still, there's
so many different approaches to it and so many nuances.
Um, it can be kind of overwhelming trying to read some of the original papers on it,
um, because they don't quite match up with, you know, current libraries in certain ways.
Um, and they don't agree with each other in other ways.
And so, um, but yeah, I think it's, um,
there's some really cool ideas there, um, just in general. And I think, I definitely think, uh,
you know, you'll be a better programmer in general. If you, if you learn a little bit
about it, even if you never use it, it's one of those things that it's just got some really different ideas. You know, especially just thinking about, thinking about events, you know, not as like a thing that
can happen that triggers some code, but it's like sort of the, this one thing that represents the
entire history and future of, you know, of, of something that can happen or something that's
changing. And you get to sort of just say how something else is computed from it as it
varies over time, but you're not explicitly talking about, you know,
some sequence of events that like when this fires,
then call this callback and then do this other thing,
you're saying how this thing is related to this other thing.
That's a really powerful way of, of, you know,
describing things changing over time. So, so yeah, I think,
I think learning about it is, is, uh, is great. Cool. Um, on the, on the topic of learning
complicated things, um, I, I personally have found reading research papers in computer science very
difficult. Um, and I imagine as like a computer science professor it's something you do a
lot and i guess the question is kind of two parts the first question is given it's such an investment
to read papers do you have a a way of figuring out if a paper is worth reading right um and then
and sometimes for me it's like you like think of papers interesting and you like get like halfway through and then you're referencing some other work that you're like, oh, I probably should read that first.
And now you're like four papers in.
You're like, I don't know if I want to read four papers.
Right.
So that's the first problem.
And then the second problem is that like once you've decided, okay, I really need to read this paper.
Do you like sit down and read it in like an hour?
Do you print it out?
What's your process? Right, right. Yeah, that's good questions. So
reading papers is difficult. It doesn't really get easier.
And there's just different things that are difficult, I guess.
So let me think.
How would I talk about sort of how I approach this?
You know, in terms of deciding whether you want to read a paper or not,
I mean, there's really no magic formula there.
You know, read the abstract, skim some part of it, you know, read the introduction and the conclusion. I don't know. You know, just try to get some sense of whether it's likely, there can certainly be situations where you're like, you know, this paper is not going to be very I'm not going to enjoy reading this.
But I think it's going to contain some ideas that might be important.
So I have to read it. Um, I think as a, as a professional programmer, that's, you know, that's going to be much rarer. Um, I think if you start reading a paper and
you're like, I'm not enjoying this, then stop, it's probably not worth it. Um, on the other hand,
you know, there's different reasons for not enjoying something. And so I guess that gets into, you know, how do you actually approach reading a paper?
I think the biggest thing that I learned, like in grad school and just, you know, in reading papers is that reading a paper is a very different activity than reading a book, even a technical book.
Because books, well, certainly like fiction, I mean, you just kind of,
you just read it and you just absorb the story as you go. But even technical books, you know,
they have a lot of space to really expand on ideas, use a lot of examples. You can kind of just
read, just sit down and kind of read through it and get a lot out of it.
That tends to not be the case with papers, especially conference papers that sort of
have a strict page limit and are trying to fit a certain amount of ideas into the space.
And so you have to understand that reading a paper and getting stuff out of it.
So I think the worst thing you can do is to just pick up a paper and just kind of start reading through it.
You have to engage with it in some active way. And so often what I do, I will like, well,
I typically will print it out because I just prefer reading on physical paper
over reading on a screen. I'll read blog posts and stuff on a screen.
But again, that's,
that's more like a book where you can just read it just from start to finish
and get something out of it. Although that's not true for all blog posts,
especially people doing stuff with,
with Haskell,
but but well,
those all print out,
right?
I mean,
so it's something that I want to engage with more actively.
I'll print it out.
I'll often print it out,
you know,
just single sided so that I can,
as I'm flipping through it,
I can use the other side,
like the blank side on
the opposite page for like scribbling notes and stuff. We're all scribble notes in the margin.
And I'll just, you know, I just, I force myself to really make sure that I really understand,
you know, what the paper is talking about. So if there's some, you know, reasoning that I don't quite understand, I've forced myself to work through it.
Um, if often, uh, what I'll do is, especially if the paper like has some code,
I'll sort of read the description and see if I can write the code myself without looking at it.
Um, and usually I can't, and then I look at it and then I'm like, Oh, and now I see how that works. And, um, you know, I,
I think that the important thing is to really fight, uh, you know,
there's this psychological phenomenon where you, um,
the feeling of understanding something is,
is sort of independent of how well you really understand it.
So you can read something and you're like, I feel like that made sense,
but you haven't really absorbed it in a way that, that you could use it or, you know, recognize it.
And so you really have to be very intentional about forcing yourself to engage actively with the content.
You know, like type up some code, like implementing some ideas or work out, you know, work out the details of some formula or equation or something.
You know, and it might be that you spend, you know, half an hour on one page, but that's okay.
Because you've probably gotten way more out of that half an hour than if you had just spent that
half an hour reading the entire paper. And even if you stop there, even if you only read the first
page and you actively engage with it, and then you just get too lost and you're like, I'm going to stop.
You've still gotten something valuable out of that paper. So I think that's the other thing to say is
that you don't have to read every paper from beginning to end, right? You can get good things
out of it by just reading as far as you can until you get to a point where you're like,
either this is boring or I can't, I'm like,
I'm missing too much background. I can't follow this anymore. Um, or I'm just too tired right
now. I need to come back to this later and then just stop, you know? Um, and, uh, you know, I,
the other thing you said about, you know, you, you follow, you're like, Oh, I need to go read
this paper first. I need to read this paper first. I don't really know how to solve that problem. You have that problem too. Yeah. I mean, and I guess it kind of, you know,
it's always hard to kind of get into a new field because there's just so much to absorb and it
feels like everything depends on everything else and you can't,
there's nowhere to, you can't get started.
It's like infinite recursion where you're like,
I need to understand this first and you just understand this first.
And you know,
part of it I think is just accepting the fact that it's going to feel like
that and that it's okay if you don't understand everything and kind of doing
the best you can, you know, don't feel like that and that it's okay if you don't understand everything and kind of doing the best you can um you know don't feel like oh i have to get down to the bottom of the stack before i can start popping you know i mixed up that metaphor but anyway i you know i have to i
have to uh you know get to sort of the most foundational paper that i can understand in
and of itself before i can get get back to you, you know, building back up. That's just, that's never going to happen. You're just going to get
lost in infinite recursion of papers and never learn what it is you wanted to learn. So,
you know, I think, I think a much better idea is to do something like,
kind of like iterative deepening, where you look at a paper, you read it or you read part of it.
If there's stuff you don't understand, just kind of write down some questions or write down some
terms that you didn't understand, write down, you know, make a list of here's, you know,
papers in the bibliography that it seemed like when they were cited in the text, it seemed like,
oh, that might be something I wanted to read about. So I looked it up and I wrote that down.
So you kind of have a list and then you kind of pick something from the list
that seems most interesting or relevant to you and look at that. Right. But you don't,
you don't have to like put this paper on hold and go read something else first.
You can still get something out of it. And then maybe you can come back to that paper later.
Once you've read some other stuff, there's papers that i've come back to three or four times because the first few times i i didn't i didn't fully understand them um
and then there's papers that i still don't fully understand so um you know it's yeah
interacting with you know other people's thoughts that they've tried to record in some way on paper,
um, which is, which, and their thoughts themselves are embedded in a complex social network of
people, you know, communicating and developing ideas together. I mean, it's just a messy,
complicated undertaking and there's no way around that. Um, and so I mean, maybe humility,
I would say is the most important attribute so maybe humility, I would say,
is the most important attribute,
just saying, I'm not going to be able to understand all this
and that's okay.
And I can still get something out of it
if I just engage actively with it and get what I can.
And after a while of doing that,
you'll find that suddenly you're like, wait, suddenly I now, if you look back and you're like, wow, I understand a lot more now,
but it's, it's such a gradual process. You can't really see it happening.
So I, I'm thinking now, I guess if the, a good strategy is humility. I'm wondering if there's like certain like benchmarks that are
like shockingly low that might help, you know, me feel better, for example. I think like people say,
I've heard like it said somewhere that like you can only spend like four hours doing really
dedicated, hard thinking work, something like that oh yeah yeah i'd be
curious to get a sense of like for um is it how many hours of reading research papers is it is
it like possible to do a day or yeah yeah yeah um yeah let me that's a really good question um
actually i can tell you exactly how much time
i spend reading research papers per week i forget what it is right now what is it what it's set to
um like i use a site called beeminder that lets you set quantifiable goals and so i have a goal
for reading research papers so right now during the, it's set to one hour per week, which is both not a lot and also a lot. I mean, it's, you know, 10 minutes a day.
But, you know, I try to make that really focused time where I'm really trying to actively wrestle
with, you know, some paper and I've got a big stack of, you know, whenever I see something interesting, I just print it out and I have a
big folder full of these things. You know, but I don't think I've ever done more than like three
hours per week. For me, that would be a lot. And at one sitting, I mean, occasionally I'll get so
absorbed in a paper, especially if I'm like, you know, I'm trying to like code it, code up the ideas in the paper while I'm reading it or something like that.
You know, I could maybe spend an hour and a half on it, but that's, that's like, you know, that's a lot.
Typically like 35 minutes or 45 minutes or 30 or 45 minutes is like, is my max.
Um, you know, and that's because of the way that I'm, that I'm, I'm reading it.
I'm not just sitting there kind of just reading.
I'm really actively wrestling with it.
Um, and I can only do so much of that before my eyes just start to glaze over. And I'll catch myself like where I,
I get into this mode of,
I just start reading and I'm not really comprehending everything that I'm
reading,
but my eyes are just going over the words and I really have to be intentional
about catching myself doing that and stopping and be like,
this is not productive either.
Either I need to get back to actively wrestling with it or just decide that's not happening right
now and I should do something else. Yeah. Interesting. Yeah. That definitely,
I guess that's kind of what I was looking for. It definitely does make me feel better.
Yeah. No, it's not like professional academics spend five hours a day reading other, no, like,
yeah, that doesn't. Yeah. And the converse of it is you'd be surprised how much you can learn in 45 minutes of really actively engaging with a paper.
You know, even if you only look at the first page.
So, you know, it goes both ways.
Cool.
Yeah, that's inspirational. Yeah. That's inspirational.
Good.
Okay, cool. Okay. So here's something that I,
it's a different question. Here's something that I've overheard.
So there are people like the people who listen to like this podcast are like
people who are semi-academic, but more like the startup who listen to like this podcast are like people who are semi-academic but more
like the startupy crowd or uh people who are but like people who are trying to make programming
languages right and some things that people people like will complain that like academics
and programming language uh like programming language academics are like you know overly
focused about like making obscure parts of haskell better right
um so i'd be curious to hear how you'd respond to to that criticism yeah i like i i feel like
it's kind of an unfair criticism but um and i could talk about it why i think so but i feel
like uh just you know to get to hear like why you think it's a worthwhile pursuit to explore more type-based attention in Haskell.
Why is that interesting?
Yeah, I mean, you know, uh, it's probably not
going to have much impact on practice, you know, right now. Um, but you know, the stuff that is
now getting, uh, picked up in industry, I mean, that was the stuff that was weird, obscure stuff 20 years ago. So, um, you know, I mean, and I guess to be fair, I'll say that, um, you know,
to some extent, maybe the criticism is justified that, I mean,
there certainly are probably examples of people, um, doing stuff,
you know, looking at things that in the end is not really that,
it's not going to make that much of a difference or it's not that interesting,
you know, just, just because they need something to do.
And I'm certainly not pointing any fingers at all, but I mean,
that's always going to be true, right? There's always,
there's always going to be some of that going on in any field. So, well,
and you know, but partly also it's kind of hard to tell the difference. You know, in hindsight, you can look any field. So, well, and you know, but partly also,
it's kind of hard to tell the difference.
You know, in hindsight, you can look at it and say,
well, that idea didn't go anywhere, but you know,
you can't really tell what idea is gonna go somewhere
and what idea is not.
Hmm, yeah, that's a good reminder that, you know,
just like core, core research uh that's that's like the the trade-off it's like it's not immediately applicable but right
yep um cool well anyways uh thanks so much for for spending the time this was a lot of fun yeah
sure you're welcome this was fun cool um well i'll talk to
you yeah maybe next year all right sounds good as promised uh here is the bonus question that i
asked brent uh after the interview over email i asked in the interview you mentioned that the
reason programming with a strong type system is difficult is because programming is difficult
and this is a very different sentiment from the quote everyone can code because it's even the reason programming with a strong type system is difficult is because programming is difficult.
And this is a very different sentiment from the, quote, everyone can code because it's easy,
end quote, movement. You implied that dynamic languages hide away the difficulty inherent to programming in the short term, while static languages force you to grapple with it up front,
which is why they seem more difficult. But in actuality they're just exposing the inherent difficulty to programming that dynamic languages hide.
I wanted to dig into this point more because my thoughts often go in the opposite direction.
On the whole, programming is harder than it needs to be due to all sorts of incidental complexity.
Would you argue that the complexity a programmer encounters while programming in Haskell is mostly essential complexity?" And his response is,
let me first say that accidental complexity is real, it is a big problem,
and we should definitely be all we can do to reduce it, for all programmers but
especially for beginners. In fact, that's a big part of why I'm developing my own
language for teaching discrete math and functional programming, because an but especially for beginners. In fact, that's a big part of why I'm developing my own language
for teaching discrete math and functional programming, because an existing general
purpose FP language would have very high incidental complexity for beginning students in such a course.
However, I think the attitude you cite, quote, everyone can code because it's easy, quote,
misses the distinction between incidental and essential complexity, and can even be incredibly damaging.
Imagine you were just learning to program, and you have been told it's easy.
But, in fact, you don't find it easy.
What will you conclude?
Since the subject is easy, the problem must be you.
You are too stupid, or you are, quote, not a computer person, or whatever.
Instead, we should be telling people, quote,
programming is hard, but, one, it is also fun and worthwhile, two, you can do it if you work hard at it, and three, I will help you.
Note, many of the best educational materials out there for teaching kids coding and computing concepts do exactly this.
They provide a fun environment, number two, with appropriate scaffolding and hints, number three, which invite the kids into
solving challenging puzzles, number one. As to whether the complexity while coding in Haskell
is more essential than incidental, I think this is often true. For example, suppose you want to
add one to every element of a list. In Haskell, you don't have to think much further than this,
map plus one list. In Java, you have to write a loop
with a brand new variable to control it.
You have to worry about getting the lower
and upper bounds right.
You have to worry about whether the reference to your list
is actually aliased by some other list reference.
You don't want to change, et cetera, et cetera.
Of course, many languages that aren't, quote,
purely functional can do this example in a nice way too. I think
my argument here is mostly about languages with good facilities for abstraction versus without,
more than about functional versus non-functional. But static versus dynamic is part of the story too.
I like the way you put it, quote, an incidental type error is one that doesn't prevent any bugs.
A program in a static language with a type error
probably wouldn't work in a dynamic language either. So the type error forces you to confront
the fact that you have a fundamental misunderstanding about your code, the problem domain, etc.
My contention would be that a sufficiently expressive static type system, that within a
sufficiently expressive static type system,
type errors are much more often an expression of essential complexity,
i.e. you misunderstand something or made a legitimate mistake,
than incidental, i.e. what you are trying to do would work fine,
but the type system doesn't let you express it.
Of course, I have no way to back this up, and people learning a statically typed language for the first time
may still feel like there is more incidental than essential complexity.
Thanks for listening and I'll catch you in a few weeks.