The Peterman Pod - Co-Creator of Haskell: Functional Programming, Thinking in Types, Useless Languages | Simon Jones

Episode Date: June 8, 2026

Simon Peyton Jones is the co-creator of Haskell (pure functional programming language) and I interviewed him about functional programming, why it matters, and his thoughts on other programming languag...es.• My ergonomic keyboard project I mentioned, you can follow along here: https://read.compose.llc/Podcast links:• YouTube: https://youtu.be/xcB_LF3cdqw• Apple: https://podcasts.apple.com/us/podcast/the-peterman-pod/id1777363835• Transcript: https://www.developing.dev/p/co-creator-of-haskell-functionalThank you to this episode's sponsor for supporting my work:• WorkOS: makes your app Enterprise Ready with easy to use APIs to add SSO, SCIM, RBAC, and more in just a few lines of code, check them out at https://workos.com/Timestamps:(00:00) Intro(00:39) What functional programming is(09:18) Downsides of functional programming(10:53) Specialized hardware for functional programming(21:47) Haskell is useless(25:59) Rust vs C(28:26) Haskell vs OCaml(35:26) Side effects in Haskell(44:26) Type systems(57:30) How the Haskell compiler works(01:04:35) Why Haskell is talked about more than used(01:09:07) Avoiding success at all costs(01:11:12) LLMs and programming languages(01:13:57) New programming language design(01:15:59) Should students continue to learn programming(01:22:33) Why Excel is his 2nd favorite programming language(01:25:04) Advice for his younger selfWhere to find Simon:• LinkedIn: https://www.linkedin.com/in/simonpj/• Wikipedia: https://en.wikipedia.org/wiki/Simon_Peyton_Jones• Personal Website: https://simon.peytonjones.org/Where to find Ryan:• Newsletter: https://www.developing.dev/• X/Twitter: https://x.com/ryanlpeterman• LinkedIn: https://www.linkedin.com/in/ryanlpeterman/• Threads: https://www.threads.com/@ryanlpeterman• Instagram: https://www.instagram.com/ryanlpeterman• TikTok: https://www.tiktok.com/@ryanlpetermanReferenced in this episode:• Haskell is useless: https://www.youtube.com/watch?v=iSmkqocn0oQ• John Backus Turing Award lecture: https://worrydream.com/refs/Backus_1978_-_Can_Programming_Be_Liberated_from_the_von_Neumann_Style.pdf• Why functional programming matters: https://www.cs.kent.ac.uk/people/staff/dat/miranda/whyfp90.pdf• Excel is his 2nd favorite programming language: https://www.youtube.com/watch?v=_M4P5M85KO8

Transcript
Discussion (0)
Starting point is 00:00:00 Why is the internet so insecure? Primarily, because all of our software infrastructure is written in unsafe languages. This is Simon Peyton Jones, co-creator of the functional programming language, Haskell, and I asked them all about programming language design. If we rewrote all of our software infrastructure and rust, things would be way, way better. I think that statically typed languages are a huge boon for LLMs. I do large-scale, systematic refactories, fearlessly, because the type system keeps me safe. In fact, would you recommend people learn how to program today?
Starting point is 00:00:35 I think people would like to be worried in the sense that I think there's going to be considerable dislocation. Here's the full episode. I wanted to start by asking you, what is functional programming in your words and why does it matter to people who might use the other languages, the imperative languages? Functional programming is about programming with values instead of mutation. So in a conventional imperative programming, computation proceeds by, like if I want to add up the numbers between 1 and 100, I have a mutable cell containing a running total. And then as computation proceeds, I keep adding to my running total.
Starting point is 00:01:19 So there's a cell that changes its value over time. Computation pretty step by step, there's a program counter. Now, that's very unlike mathematics. If I say 3 plus 4 times 7, you don't think of a mutable cell that changes its value over time. you think, well, 3 plus 4 is 7, 7 times 7 is 49. Just the answer is 49. So in a sense, it's more declarative than imperative. Another way to think of it is it's more like what a spreadsheet does.
Starting point is 00:01:45 In a spreadsheet formula, if you put in a cell, you'd say in A1, you put the formula equals A2 times A3 plus 7. Well, then that's just the formula that is evaluated, and you expect to compute the values of A2 and A3 automatically, before computing A1, it wouldn't make any sense to compute A1 from the old values of A2 and A3. And there's no notion of a program counter. There's no notion of do this and then do that. It wouldn't be sensible to say equals print X plus three.
Starting point is 00:02:18 Because who knows when or even whether that cell will ever, that formula will ever be evaluated. Now the surprise is that it is possible to do useful things in such a limited language. when I would learn programming, I learned it by writing machine code in which the fundamental mode of computation is registers and program counters, right, and mutation of registers as you go along and memory. Like computation and mutation seem to be inextricably interwoven.
Starting point is 00:02:46 That's how computation gets done. So it was a surprise to me when I learned that, well, it is possible to do useful things with functional programs. I still remember that amazing revelation. Now, in fact, one way to look at it is this. Right back in the late 1920s, early 1930s, Alan Turing was doing a PhD in Princeton under the supervision of Alonzo Church.
Starting point is 00:03:16 Now, Alan Turing, as we all know, invented the Turing machine, which is fundamentally about mutation. It has a little tape, and you can read things from the tape, and then you write back new things onto the tape, and it has an immutable program. Now, Alonzo Church, at the very same time, was working on something he called the Lambda calculus, in which there's no motion of mutation.
Starting point is 00:03:36 This is, the lambda calculus is like the essence of functional programming. Programs execute just by reduction. Now, so these were two different models of computation. You might ask, what can you compute with the Turing machine, and what can compute with a lambda term? And the astonishing thing that turned out, which is completely non-obvious, is that anything you can compute with a lambda term,
Starting point is 00:04:01 you can compute with a Turing machine, and anything you compute with a Turing machine, you can compute with Lanta calculus. So that means that the two are identical from the point of view of their computational power. Big surprise to me. Okay, so they're equally powerful, and then the question is,
Starting point is 00:04:17 which might you want to choose? As I say, I started life from my baseline of imperative programming, of course that's the way you write programs, And functional programming is a kind of weird, anomalous, rather nerdy, academic thing. But I became hooked on the idea that by excluding side effects by default, that is, imperative programming has side effects by default, by excluding side effects by default, we could make programs that are easier to write, easier to maintain, easier to reason about.
Starting point is 00:04:48 And then the question is, what are the practical obstacles? Is it slower? Is it less convenient and so forth? in effect, I've devoted my research life to exploring that hypothesis. The wonderful thing about being a researcher is you can take something, which at the time, this is back in 1980, back in 1980, a functional program was very nerdy, geeky academic, but I was allowed to spend my professional life just saying what would happen if you took that one idea programming with values and just ran with it. Where would you get?
Starting point is 00:05:24 and I think we've got somebody somewhere pretty interesting. I don't want to say that it's better fundamentally because after all Haskell on that journey started with functional programming then we learned how to mix functional imperative programming together
Starting point is 00:05:37 in a way that we might talk about later so it's not really an either-or it's more of a both-hand in the end but my position now is as I often say when the limestone of imperative programming has worn away
Starting point is 00:05:53 the granite of functional programming will be revealed underneath. And why do you say that? Well, functional programming is the language of mathematics. That's how we fundamentally think about things when we want to be formal. Indeed, if we want to reason about and say what does this imperative program do,
Starting point is 00:06:10 we have to wheel out a lot of mathematics and, you know, hoar triples and so forth. But that's all in the world of maths, which is just functional programming. Functional programming is just mathematics brought to life and made executable. So you might wonder, I suppose you may be saying, well, isn't imperative programming just enough?
Starting point is 00:06:32 And I think that there you have to look at the broad sweep of things. I think what's happened is that from the world of functional programming, lots of ideas have got adopted into the imperative arena. So they grew up over here in functional programming. and then they have been adopted by the imperative programming things. So you might think garbage collection, you might think lambdas, you might think language integrated query, you might think about lots about type systems, polymorphism and static typing, tons of that.
Starting point is 00:07:05 So at least it has been an intellectually interesting experiment that has been a very fertile laboratory, which to grow up ideas that can infect the mainstream. Now, my instinct now is that we should, should start from functional programming and do imperative programming where necessary. I think probably the mainstream is we should start with imperative programming and sort of add bits of functional programming where possible. And I'm not going to say that they're wrong to do that,
Starting point is 00:07:34 just to say that I think there's a pretty interesting dialogue. Now, you do have to think very differently about the act of programming, right? You do have to rewire your brain quite a bit. But just let me, this is something we might want to come back to. I said easier to maintain. As programs get old, think of a 10-year-old program. If you have a piece of 10-year-old code written by somebody who's long since left your company
Starting point is 00:07:59 that mutates some more or less global variables, then you might be a little bit afraid about, oh, and there's other bits of code scattered about that also read or write those global variables, then you might be a little cautious about making modifications, less, oh, if I put these two things, if I do B and then A instead of A and then B, maybe the global variable that I read somewhere deep inside the code is now not have the value that it was expected to.
Starting point is 00:08:30 So reading and writing shared variables that are visible outside the scope, somewhat global variables, is a form of coupling between bits of code that is very invisible. Very invisible. So functional programming forces, that to become visible. It forces it into the open. And by the act of forcing it into the open, that strongly encourages a style of programming
Starting point is 00:08:53 which you don't have such interactions because they are painful to manage. So it encourages a style in which programs execute in modular box without interacting. Now, every imperative programmer would want to do that, but I want them to do that in a provably secure way.
Starting point is 00:09:13 So you can know that these programs. I'm not interested to act rather than just say, I've done my best. Then I guess on the flip side then, what are the downsides of functional programming language? Oh, well, it can be very tiresome and inconvenient, right? So you're somewhere deep in the, you know, a subroutine of a subroutine of a software stack, and you want to reach out and, I don't know, grab the time of day. Oh, dear. That's a side effect.
Starting point is 00:09:43 But we're in a pure functional language, the time of, and indeed, there's a good reason because everybody else in the stack is assuming that if I call this function applied to, you know, seven, it will give me the same answer if I call it, call it again applied to seven. But if it could interrogate the time of day, suddenly it might give a different answer. Right. So as simple a thing as I was asking for the time of day, invalidates a whole set of assumptions and so is not by default allowed at all. And that can be a convenient because you always say, look, guys, all I want to do is get hold of the time of day. Don't give me grief. And so every functional programming language, including Haskell, provides you with a little trapdoor. Haskell's is called unsafe performio. And it says, trust me, I'm going to do something that has side effects, but you can pretend there weren't any. All right. And it's called, it is literally spelled, UN, S, A, F, E, Perform I-O, the long name that has unsafe in the title so that you, the programmer, taking on the obligation to make sure that what you're doing, is okay. Whereas in imperative language, everything you do could be unsafe before my ohish.
Starting point is 00:10:52 I was watching one of your lectures and you did go over a little bit the history of functional programming languages and you mentioned somewhere that there was this idea of people building hardware for functional programming languages. It just made me curious because I didn't, I only know the Von Neumann architecture. Do you have any idea what that might look like and some high-level thoughts? Well, I did. I now think it's probably a bad idea.
Starting point is 00:11:25 But back in 19, when was Backus's Tewing Award lecture? I think it was 1978 or about. What was it called? Can programming be liberated from the Von Neumann style? That's it. Now, he was saying two things. Firstly, he, the designer
Starting point is 00:11:42 Now, of Fortran, a quintessentially imperative language, the designer of Fortran was saying, guys, we should do functional programming. That was what the talk was about. And he introduced a language called FP for functional programming. It was a very particular, somewhat idiosyncratic, functional programming language, but there it was.
Starting point is 00:12:04 So that was rather amazing, right? This was 1977. He was saying, guys, give up on imperative programming. just do functional programming. Here's how you do it. But he also said in the same lecture, we might imagine designing new hardware to execute this new kind of language.
Starting point is 00:12:23 So if you like, we've got Turing machines, they give rise to register machines and microprocessors and so forth, maybe Lambda calculus, perhaps if we designed a machine from the ground up to do Lambda calculus, it would look different. So that was, of course, very exciting.
Starting point is 00:12:39 and a similar kind of thinking led to an actual commercial machine called a LISP machine that was a piece of hardware that was designed by symbolics to execute LISP programs LISP is a mostly functional programming language and so at the time I was
Starting point is 00:12:58 in my early 20s we were thinking, oh, crumbs, what would a piece of hardware look like that was primarily designed to execute functional programs? We'd have a different kind of instruction set and architecture. So the newest we got to it, actually,
Starting point is 00:13:12 was, well, there was a big strand of work then that was the called the MIT Dataflow project. So data flow languages, functional languages, they're definitely the same kind of category, right? And so the MIT had this great group led by Arvind, called the MIT Data Flow Group, and they designed, in the end, a machine called Monsoon, that was specifically designed to execute data flow programs,
Starting point is 00:13:38 that is functional programs. It was based on, you know, a token store and matching and tokens flowing around and getting matched up and executing. You might imagine data flowing through a graph. When three arrives at a plus node and four arrives at the other input of the plus node, bam, you could fire that plus node, right? And the hardware did all of that. At a similar kind of time in England, my colleagues, Thomas Clark, Joe Stoy and others,
Starting point is 00:14:04 built something called the Skii machine, skim. There's a paper about this in the FPC. conference and Skim was designed to execute SK combinators so at the time David Turner has written lovely paper in which he described how to take Lambda calculus programs and translate
Starting point is 00:14:23 them into SK combinations so you can read about that in my book it's a very simple translation that you can give the whole translation in half a dozen lines but then you have this messes of this mess of S's and K's and S and K reduction is very very easy so
Starting point is 00:14:40 like the machine code of functional program. That one way to think of it. What is a SK combinator just for context? There are three combinators, SK and I. So, and they have simple reduction rules. Here they are. I of X equals X, KX, Y, equals X. SX, Y, Z is X of Z applied to Y of Z.
Starting point is 00:15:01 End of story. That's all. So very simple, right? So now all I've got to do is, take my lump to translate it into a giant tree of SK combinators, right? S applied to K, apply to I of 3, and S of Z, just a huge tree of S's and K's. Right? Now simply apply the rewrite rules I gave you.
Starting point is 00:15:22 And that's, that is program execution. It's rather astonishing that that can execute arbitrarily complicated programs, but it can. Do you think that's rather amazing? Now, then I can take an arbitrary Haskell program. I can translate it into Lambda terms, and I can translate there's Lambda terms. into S&K, very simple transformation, and I can just run those three rules, and it'll produce the output of the Haskell program.
Starting point is 00:15:47 That's pretty amazing. And indeed, there is an implementation of Haskell that uses exactly this. It's called MicroHS, and my colleague, Leonard Augusten, has built it, and its execution mechanism is SK-commodator reduction. And if you sit in micro-HS, you can ask you to show you the S&Ks that it produces.
Starting point is 00:16:04 If we had a shared screen, I could probably show you. So, long story short, then. the Skii machine was designed to take SK trees, big trees of S&K combinators, and just execute them directly. So it was built in hardware, and it ran perfectly well, reasonably fast. Now, why didn't this catch on?
Starting point is 00:16:29 Why don't we have functional programming machines? I mean, at the time, it was a big idea. We even had a conference called Functional Programming Computer Architecture, FPCA, It was right there in the title of the conference. But slowly we became aware that what was really happening is we were doing at runtime what we could do instead at compile time. And that is always a bad idea. Imagine an interpreter for, I don't know, Pascal or something.
Starting point is 00:17:01 Or for Java. We can compile Java to bytecode and we could interpret the bytecode. right? So then we're doing something at runtime. At runtime, the interpreter is looking at the bytecode saying, dispatching to say which instruction, blah, blah, blah, blah. Now what does EJIT do? It takes a sequence of bytecode and says, oh, no, no.
Starting point is 00:17:22 I'm going to take that sequence of bytecode to compile it to a sequence of machine instructions that, when executed, will do the same thing, right? Much faster. Because it can take advantage of bytecode sequences, for example, to say, you know, if you swap and then swap, it's a no-op or something like that. So what, in fact, it turned out is this whole S-K-I business, and any other, and indeed, data-flow machines also, were simply doing at runtime what you could do better at compile time. We were building an interpreter in hardware.
Starting point is 00:17:58 So don't build the interpreter in hardware. Instead, build a compiler that translates your Lambda term into a sequence of machine instructions that when executed will behave as if you had done this reduction business. And then you might think, well, machine instructions for what machine? Well, from a practical point of view, it was very hard to compete with Intel. Right. They were just spending billions of many years on making X-86s go faster and arm likewise for arms.
Starting point is 00:18:32 So we can leverage all of that work by compiling into their instruction set. But even if you say you are God, you are the chief executive in Intel and can tell them to add new instructions or computation mechanisms to support functional programming, what would you add? Not very much. Little bits to do with garbage collection barriers, I think, and such like, but nothing major. So, in other words, I think the whole build hardware to execute functional programming directly. turned out to be a mistake. Inspiring mistake, fun mistake, I had a great time, but a mistake.
Starting point is 00:19:15 It's better to build a compiler. I would have thought there might be some unique opportunities for parallelism, given the immutability in these functional programming languages. So, yeah, you described that graph or that SK combinator graph. I imagine you could, you know, execute that all in parallel,
Starting point is 00:19:37 assuming there's no connections across, the graph. And indeed, that was the data flow machine, the MIT data flow machine was all based on that idea. So throw the whole graph into the token store and just run all the nodes that are runable. Right.
Starting point is 00:19:51 But that's incredibly fine grade. You've got to imagine this big tree of a million nodes and your processor is sort of wandering around over at doing little things in parallel. There's a lot of memory traffic going on there. And these little threads are operating on the same bit of tree
Starting point is 00:20:06 then there's synchronization costs. So very, very fine-grained parallel computation like this turns out to be impractical. Or not impractical. You could do it, but it's very slow. So the data flow people, their history, if you look at the history of the MIT Dataflow project, they started with micro-parallelism. Every individual instruction was a separate parallel computation that might take place and the token sort would match it up.
Starting point is 00:20:36 And then they built more and more compiler technology. that group these things together into larger units. So they expanded from micro threads of single instruction threads into 10 instruction threads or 100 instruction threads, right? But even that never really caught on. So you're right, though, that if I say in a Haskell program, E1 plus E2, then I can do E1 and E2 in parallel. And GHC does support you in doing that.
Starting point is 00:21:04 But you nowadays, rather than expecting the compiler to do that automatically for every sub-expression, you can spark a computation. You say E1 par E2, and that says do E1 and E2 in parallel. There's a project at Carnegie Mellon for a strict language called ML that has a very good variant of parallel ML going in a similar way. So essentially we've moved away from the dream of automatically getting parallel very, very fine grain to programmer, we give programmer clues about words,
Starting point is 00:21:39 good idea to do parallelism, but still the compiler will try not to fork two tiny grains. I was searching on YouTube, and I've searched your name, and this video came up, and it said, Haskell is useless, Simon, Baten Jones. And it's a video from a long time ago.
Starting point is 00:21:59 Someone's got a low-quality, handheld camera, There's a group of researchers that are all talking. Butler Lambson is across the table, and it's a fun little video. And in the video, you lay out this two-dimensional graph, where on one dimension, there is the useful versus useless axis, and then on the other dimension, there is the safe versus dangerous, and then you're placing programming languages on this two-dimensional graph. And I think you started by putting C as it's very useful, but it's incredibly dangerous.
Starting point is 00:22:39 And then you put Haskell on the polar opposite side. You said it's useless, but it's very safe. What are the least safe and most useful languages and why do you place them there, like C, for instance? Well, yeah. So let's see, in C, you program my mutation. So it's unsafe in the sense that any function can mutate any variable, at any time. It has a lot, you pass pointers around a lot and functions mutate
Starting point is 00:23:05 the memory pointed to by those pointers and moreover, typically they can mutate it anywhere, there's no array bounce checks or anything. So it's kind of like super unsafe and in fact, and this is demonstrated by fact, can you imagine all of these exploits that we get every day, right?
Starting point is 00:23:21 That, you know, mythos is discovering in huge numbers, but we've had, you know, why is the internet so insecure? Primarily, because all of our software infrastructure is written in safe languages. If we I mean, if we'd written all of our, you know, internet software
Starting point is 00:23:36 and operating systems in Haskell or maybe in O'Cammel or ML, 99% of all these exploits would be removed by construction. It's like we've built a boat out of paperclips and we're surprised that it's leaky.
Starting point is 00:23:55 I mean, you shouldn't build boats out of paper clips, right, because they have holes in them. You should build it out of a secure substance, right? But then it's too late. So we spend incredible amounts of human ingenuity and effort patching the holes in our boat built a paper clips.
Starting point is 00:24:10 It's tragic. It's tragic. How much effort and ingenuity and money has been lost and waste of resources just because we wrote our computational infrastructure for the world in an insecure language. That's what I mean by
Starting point is 00:24:26 unsafe. I mean, are there not vulnerabilities? Like if I wrote something in Haskell, I mean, There's got to be some new type of... Of course there are vulnerabilities. I just said 99%. I didn't say 100. If I write a Haskell program that says,
Starting point is 00:24:40 receive message, if the message says, tell me everything, then spit out my entire database in reply. No language can stop you doing that, right? But if you look at the program and it doesn't have any such things, right? So, I mean, so, you know, nothing can prevent you against high-level attack. to insecure programs. Or, I mean, another example might be deadlock, right? Two services, no matter how securely written,
Starting point is 00:25:09 if A waits for B and B waits for A, deadlock. Sorry, no, and no language is going to stop you doing that. You might hope for some high-level verification tools. But it's like, surely if you're trying to do something hard, like prove that that doesn't happen, you want to have a foundation that is, you know, in which you've got some bedrock to stay. stand on. If you're standing on sand and trying to prove some advanced property, it's very,
Starting point is 00:25:38 very difficult. But good point. I'm not talking about 100% security. Absolutely not. But how many exploits are based on buffer overruns or, you know, point of manipulation that's gone wrong? If you couldn't have a buffer overrun, you couldn't do point of manipulation that got wrong, those those exploits just wouldn't exist. So, I mean, C's maybe half a century old now. What about more modern versions of those lower level languages like Rust? Much, much, much, much better, right? If we rewrote all of our software infrastructure and Rust, things would be way, way better. I'm not actually even sure whether Rust has a raybounds checks built in, but it must have the ability to promise that you're not indexing out about.
Starting point is 00:26:29 I don't quite now, quite now. But if you compile a load code with that switched on, you are way better situation, way better. So, yes, this is not just functional programming, but you did ask about why I thought C was in insecure, unsafe language. If we imagine that graph, there's that the upper right quadrant, which is useful and safe. And you described that as Nirvana in the video. And I guess maybe over time, I mean, C's somewhat really unsafe, really useful.
Starting point is 00:27:01 Yes, so C, I mean, Rust has moved a lot. along the axis from useful but very unsafe to stay useful and become safer. Now Haskell started life as being very safe but useless. So it's worth just rehearsing that because in the first version of Haskell, there was no I. The only that a Haskell program was a function of type string to string. It's functional programming after all. It could do NIO. All it could do was take a string and produce a string.
Starting point is 00:27:31 So obviously that's not very useful. it's a little bit more useful. A language it is very, very safe and completely useless is no-ob. There does nothing ever. Very safe, but very useful. Haskell was a bit better, right? At least it lets you
Starting point is 00:27:47 apply a function to a string and get back another string. So of course we then worked on how could we do I.O. in Haskell in a safe way. That will lead to, you know, monads and stuff. But, but, so just as the, you know, we're moving from C,
Starting point is 00:28:03 horizontally to go safer and safer, but staying useful. So from Haskell, we're moving sort of vertically to get more and more useful without stopping being safe. And Nirvana is when we sort of meet up, right? But the point is not to say one is better than the other, but just to say that we both seek things that are useful and safe. When I think of functional languages, I guess in the mainstream or what people are kind of thinking about, I hear Haskell. I also hear OCamill.
Starting point is 00:28:35 How do these two programming languages compare if someone was trying to pick a functional language to work with? So OCamill is a strict language that it's called by value. So when you say F applied to 3 plus 4, it'll add 3 plus 4 and then call F. Forget something right. In Askul, if you say F of 3 plus 4, it'll build a little suspension that says, well, if you ever need this 3 plus 4, you can evaluate it, but maybe you'll never need it, and then it calls F.
Starting point is 00:29:01 That's a lazy language. Now, so because O'Call is strict, it has a defined order of evaluation. So if you say F of 3 plus 4 and a second parameter 8 plus 9, it'll evaluate 3 plus 4 and then 8 plus 9 in that order. So it makes sense to say, you can say F of print hello, comma, print goodbye, right? Because you get hello and then goodbye printed. So once you have a defined order of evaluation, then it's pretty easy. you to incorporate I.O. Even though it quotes
Starting point is 00:29:38 a functional language, O'Camel allows you to do IO without saying unsafe perform IO or anything. In fact, it has a defined order of evaluation and it's perfectly kosher to do that. As a sort of unintended
Starting point is 00:29:54 consequence of being called by value, it was also, by default, impure. Now, good O'Camel programmers won't use many side effects, but O'Camel doesn't prevent you. Haskell prevents you. Why does it prevent you? Because if we'd allowed you to say F of print hello, comma, print goodbye,
Starting point is 00:30:16 those would be thunks, like, not yet evaluated. Right? So if F happened to evaluate its first argument and then it's second, we'd print hello and then goodbye. If it evaluated its second and then it's first, we'd print goodbye and then hello. If it didn't evaluate either, we wouldn't print either of them. That doesn't sound very good if you want to control what I always happen.
Starting point is 00:30:36 right, laziness forced Haskell to stay pure, strictness allowed O'Camel, which grew out of the ML tradition, by the way. ML is another functional language that predated Haskell. It was always strict and it always had I.O. by default. It wasn't a goal, but that's just the way it worked out. At the time, nobody thought about it. It was just obvious. So that's a big difference between O'Camel and Haskell, right? is that
Starting point is 00:31:03 now then, since then, they've both grown up. Haskell's gained sort of monads. O'Cammels gained lots of fancy type systems. Some of them,
Starting point is 00:31:13 you know Haskell and how O'Chamel have lurked from each other. O'Chamel has grown an interesting effect system recently and a whole lot of new extensions. It's an absolute hotbed of innovation at the moment, Ocammel.
Starting point is 00:31:24 So I view O'Cammel as kind of siblings, brothers and sisters. We love each other. We learn from each other. We compete with each other, all of the things that siblings do. But they're not the same, right? So siblings don't say, I'm just better than you.
Starting point is 00:31:40 You shouldn't exist. We say, let's enjoy our life together. And that's the way it is. You describe the laziness and the strict. And as a programmer, my initial thought is I would like to control the order of execution. And when I say print X and then print Y, I wanted to have that order. otherwise it would be a little unintuitive. What is the main benefit of laziness?
Starting point is 00:32:05 Why is laziness good? Well, John Hughes did this rather well. Way ago, 1980-something, he wrote a program called Why Functional Programming Matters. And one of its main thesis was that lazy evaluation lets you compose programs in a particularly modular way. So imagine a program that is, I mean, his classic example,
Starting point is 00:32:28 is a program that plays chess. Right? So one thing you could do is you could imagine building a tree of all possible moves starting from the position we're at. It would be a very, very big tree. In O'Camel, it would be too big because first of all I compute the tree and then I'd start deciding what move to do. It couldn't possibly do that because the tree of all possible moves, you know, has more nodes in it than the number of protons in the universe. So let's not do that. Now, if instead you could build the tree, oh, having got this tree, supposing you had it, you could then walk over the tree saying,
Starting point is 00:33:02 oh, let me do some minimaxing and saying, oh, this looks like a good position, let me go this way. I could explore the tree, right? Now then, so in O'Kammel, then, you would have to put the tree generation and the tree exploration in one function. In Askell, with lazy evaluation, you can generate an infinite tree,
Starting point is 00:33:24 and then the explorer that prunes the tree and explores just the bits of it that is necessary, is completely modularly separated. I can build a different generator and a different pruna. They're just completely separate programs. So lazy evaluation is a very powerful glue that lets you glue together to programs that you'd like to be distinct.
Starting point is 00:33:49 Strict evaluation forces you to merge them together. Yeah, this reminds me. I mean, so in Python, for instance, they have the idea of a generator where you could... lazily retrieve things. Every strict language, every, you know, serious strict language, has lazy evaluation in it. They're called iterators or generators. And so does O'Camel.
Starting point is 00:34:11 But in Haskell, that's the default. Now, instead, in Haskell, so this is a bit like, again, the two are converging on the middle, right? Haskell, lazy by default, but you can make things strict. You can put in exclamation marks to say, please evaluate this before the call. or the IOMO now to say value but that's a more brutal strict strict amortation so in Ascle you can make things stricter in O'Connell you can make things lazier
Starting point is 00:34:38 right so it really boils down to is what's the default we both want to have a mixture of the two and then it becomes a bit cultural as for which you prefer I don't know some people sometimes ask me they say well if you were designing Haskell again would you make it strict
Starting point is 00:34:58 by default with really good support for laziness and I often say well I might yeah it does seem attractive because frequently I found myself
Starting point is 00:35:08 cursing laziness as an implementer but I strongly suspect that 10 years after that I'd be thinking man if only it was lazy by default oh when I say strict by default I would all definitely mean
Starting point is 00:35:21 strict but pure right no side effects a few times in our conversation you've mentioned the word monad, and it seems like it allows us to do the side effects. What is a monad? And how does it help us do side effects and preserve ordering?
Starting point is 00:35:41 One way to think about it that's very easy to understand and use is just to imagine that the do notation is somehow built into Haskell. So you can say, do print X, semicolon, print Y. and that has type i.o unit. So a value of type io unit means I do some input output and return a value of type unit. An expression of type i.o-int is an expression that when you run it, we'll do some i.O. and return an int. Okay?
Starting point is 00:36:18 So do lets you combine i.o performing computations together. So, print 3 has type I.O. unit. It does some I.O., namely printing 3. Get char has type I.O. Char. It does some I.O., namely, reading a character from standard input and returns a character. All right? Notice that's different from just char. So, you know, quotes X, quote, that has type char.
Starting point is 00:36:52 It's just the character pure, right? Get char, the I. The I.O. Performing operation has type I.O. chart does some input output and returns a character. Okay. The do notation lets you combine together I.O. performing computations. So if you could say do, and then you say X left arrow get char, semicolon, put char X. then that X left arrow get char that says run the get char computation and get me the character call it x the put char x says run the put char x
Starting point is 00:37:34 and we combine them together with the do notation that combines two computations to make one i.o performing computation right so but these things are completely first class that's what's new about monads compared to just make it into into c X left arrow get char semicolon put char x. That's a computation which type is iow unit. Let me give it a name. So I can say let foo with type arrow unit equals that do.
Starting point is 00:38:06 Now I can pass foo as an argument to something. I could put foo in a data structure. I could return foo as a result. It's a value just as much as three or plus. In particular, for example, I could say do, Fou, semicolon, foe. That takes foo, uses it twice. Each time, do fu semicolon, foe says, do foo, and then do foo again.
Starting point is 00:38:35 Right? So I'll read a character and print it, and then read a character and put it again. So these values of type IOT, for some type T, are first class values. All right. in C, I can't take X-colon equals 3 semicolon, you know, Y plus 4, and pass that as an argument to something and expect it to happen wherever it's used, right? It's not a first class value. When I first was learning this monad idea, it seems like you've said in a few places, it's a way to do the side effects but keeps the language pure. But when I saw it, my first thought was it's almost like this little.
Starting point is 00:39:18 you know, place where we segregate the dirty things we want to do, I guess. But then how does that keep the language pure? Oh, because, because. Well, yeah, we are lying it. But look, if your function has type int arrow int, can it do any I.O.? No. No. If it has typed int to I.O.int, it could do arbitrary I.O.
Starting point is 00:39:42 So yes. So it's nice, pure int to int-to-int function or dirty into-ink function. So you may say, perhaps you're about to say, that's a bit of a blunt instrument. Either completely pure or completely dirty, right? It gets you an awfully long way. But nevertheless, it would be cool if you could say, oh, I am an int to effective doing reading of files only, int. So you'd like to, in the type, you'd like to say,
Starting point is 00:40:20 what kind of effects can it have? Can it throw exceptions? Can it, you know, spawn new threads? So you'd like to enumerate the effects this computation could have, right? That will be cool. That's called an effect system. And there's, you know, bazillion programming language papers about effect systems. And it turns out that you can indeed, in the type system of Haskell,
Starting point is 00:40:48 and indeed, O'Connell is rapidly becoming safe. You can express not just all or nothing, does it do IO game over, but rather which particular effects does it do, including no effects at all. That's pure. A good place to start is a library called Bluffin, which my colleague Tom Ellis has designed. It's a very nice take on how to do effect systems in Haskell. I guess the purity comes from that this, this dirty stuff is signaled through the type system.
Starting point is 00:41:25 Correct. Correct. So we can do it, but here it is and be aware. Yep. And because you have to thread, you know, because the type system gets in your face, you know, you were trying to, like Map, say. No.
Starting point is 00:41:42 Map says, I apply a function to every element of the list. Well, so it has type A to B to list of A to list to B. if you want to apply an I.O. performing function, so the function you're applying has type like it to I.O. of char. You could map that over a list, but you just get a list of I.O. of chars. That hasn't done any I.O. yet, right? You want something that says, take a list of I.O. Chars and perform those actions one at a time. So I want a function that goes from type list of I.O. char to I.O. of list of char.
Starting point is 00:42:16 right and you might want to perform all those actions top to bottom or maybe bottom to top who knows that's what this function of type you know so yes so you could do it in various ways so sometimes it gets in the way right you say oh shit you know can't I just use map well in Askell no sorry you're going to have to do a little bit more work to tell me
Starting point is 00:42:39 in what sequence you want your effects to happen because by default Haskell does not specify sequence So, you know, adding monads to control effects does get in your face a bit. And that's a tax we pay. In effect, that's part of the big experiment that Askell is doing is to say, suppose we up front say we're willing to pay that tax. You know, how many followers can we get? And the more, but I will note that monads have infected quite a lot of other languages.
Starting point is 00:43:14 like F-sharp is definitely called by value in pure language, and yet F-sharp had these workflows that were definitely monads, right? And monads have appeared in Scalo and in many other languages. So something that's very monad-like has appeared lots elsewhere. It's been a very unifying concept. Open AI, Anthropic, Cursor, and Versal all use this product to make their lives better. And the problem it solves is when you're building SaaS or an AI product and you want to sell to other companies,
Starting point is 00:43:44 there's all these requirements you need to meet. There's SSO, there's SCM, there's RBAC, there's audit logs. These are all things that take time to integrate, but aren't the main focus of your app. WorkOS is an API layer that lets you meet all of these requirements in just a few lines of code. So let's say you have a new SaaS product and you want to sell to other companies.
Starting point is 00:44:05 WorkOS will solve all of these critical feature gaps for you. You can check them out at WorkOS.com learn more and get started. And I appreciate them for supporting my work and sponsoring this podcast. When I read about Haskell and people's perception of Haskell, one thing that keeps coming up is that the type system is really powerful. And so I kind of want to ask you, maybe just even on the highest level, what is a type system in your words? Okay. So what does a type system do? It lets you reject silly programs up front. So if I have a function that adds one to things
Starting point is 00:44:47 and I apply it to a character or to an I.O. computation, I'd like to, that's going to fail at runtime. Because I can't add one to a character. Or maybe you overload plus, maybe I reverse a list or something. If I give, if I go to a list reverse function, I give it something that just isn't a list. then it's a bit silly to allow that and only fail at runtime.
Starting point is 00:45:13 So fundamentally, type systems are about rejecting at compile time, programs that you do not want to run because they will fail at runtime. Okay. Now, we've had static type systems for a long time, like going back to Pascal and earlier. But simple type systems are annoying. because they get in the way. Imagine a function that reverses a list.
Starting point is 00:45:43 In Pascal, you could write a function that reverses a list of integers, but if you wanted to reverse a list of characters, sorry, you can't, that function that reverses a list of integers, it has type list of int to list of int, so you can't apply it to a list of characters. Game over.
Starting point is 00:46:02 You have to write another copy of the code with a different type. That's a bit annoying. So, what are we going to do? We need polymorphism. We need a more powerful type system. We want to give reverse the type for all A, list of A to list of A.
Starting point is 00:46:18 So that up front says, I work for any type A. You give me a list of integers? Fine. I'll produce a list of integers. You give me a list of characters. Fine. I'll produce a list of characters. Notice that's much better than just list to list.
Starting point is 00:46:32 I want to keep the fact there's a list of integers. So I know if I apply, you know, when I look inside the list, I know I've got an integer. But also, if I just list the list, I might get back a list of some completely different type, but I know that reverse returns a list with the same type of things. So, parametric polymorphism, super valuable. If you want a psychic type system, you must have parametric polymorphism. The message is, if your type system is too simple, it gets in the way.
Starting point is 00:47:02 Very important lesson, because it means that we cannot really say is a type system. system useful or not, unless we say which one? Because we know that weak type systems are very inconvenient. It must at least have parametric polymorphism. And that is another idea that was born in the world of functional programming. It was born in ML, incidentally, Robin Milner's famous dictum, World Type Programs, don't go wrong. ML was the first, I think, the first parametarily polymorphic functional programming language.
Starting point is 00:47:33 But generics in Java and object-oriented programming more generally is exactly the same idea. Right. So there's an idea that was born in functional programming and made its way into the mainstream. And you mentioned polymorphism. So there's this parametric polymorphism in the type system. But in the example you mentioned where maybe you want to do an operation on a list and then the type of the input changes. I was just thinking, what about when people do polymorphism in the classes? Okay. So now you're into a whole more complicated world, right? So as soon as you say class, you're talking static type system again, right? So object-oriented programming is another approach to polymorphism.
Starting point is 00:48:16 So in an object-oriented language, we say if we have a Ford that is a car and a car is a vehicle, then anything that works on vehicles should also work on cars and should also work on fords. So the code that we write for cars works unchanged. for vehicles and for for for for for. Okay. Now, that's a form of polymorphism, not parametric polymorphism. That's what you might call object-oriented problem,
Starting point is 00:48:49 or subtype polymorphism. Okay, so now we, but polymorphism in the sense that, the same code, the same executable code, actually the same machine instructions, work on values of different types. Okay, in both cases. Both reverse a list, same machine instructions. Code that works on vehicles, works on things on Ford, same machine instructions, right?
Starting point is 00:49:12 Okay. That's what polymorphism in general means. Parametric polymorphism means this for all A, list of A to list of A stuff. Subtype polymorphism means, if it works on vehicles, it works on any subtype of vehicles. Okay? Now, the interaction of the two, which you get by adding generics to an object-oriented language, is pretty complicated. Oh, and by the way, adding side effects as well.
Starting point is 00:49:39 And that's why you're... And so your question involving classes and superclasses and so forth is smack in that complicated world. And I'm not sure it'll be very fruitful for us to... We'd have to get a lot more details of the type system sorted out to know what to say. But at this very high-level overview, the big point I'm trying to make is
Starting point is 00:50:01 weak type systems get in the way. Type systems are meant to reject programs that will go wrong. But my function that reverses a list of integers, it will also reverse
Starting point is 00:50:12 a list of characters. So it's tiresome to be told no, that is a bad program. I want to be able to write that it has that for lay, list of A to list of A. So the idea of making the type system
Starting point is 00:50:22 more complicated is to say programs that you want to run, you can still write in your static type system. Now, you might say well, blimey, if that's all, why don't we just get rid of the static type system altogether? Now we could run all of those programs.
Starting point is 00:50:37 But the trouble is you can run too many programs now. You can run programs that will crash at runtime. And that's very, very, very bad. And we all know the costs of programs that crash in deployment that you could have crashed before you even started to run them, before you even ran your first test. Right? Before you even linked it into an executable, that's really. good. But the biggest benefit
Starting point is 00:51:03 of a static type system, in my humble opinion, is maintainability. If you have a program written in, I don't know, Pearl or Ruby or LISP in its basic form, and it was written 15 years ago and the original author has left
Starting point is 00:51:20 and all of the people who were involved at the time it was written have left, then that program is very difficult to maintain. And it becomes almost immutable Nobody dares change it anymore. What they do is it's an immutable piece of software and you do that stuff around the edges to impedance match what you really want to do
Starting point is 00:51:39 to this now immutable blob. So of course, you have lots of tests. So test your development, I'm totally with it, I've done development, all of that. But still, GHC, for example, is itself written in Ascot. It's 35 years old. And yet I do large scale, systematic refactings of it,
Starting point is 00:51:58 you know, fearlessly. because the type system keeps me safe. In fact, often what I'll do is I'll change a few types and then start compiling. And then a sort of wave of changes propagate through forced by, you know, I just get type errors, so I know what to do. Whereas the thought that I've changed the representation
Starting point is 00:52:16 of this data structure a little bit, but I've added a field to this data structure, where in the entire compiler might that field be read, written, or freshly allocated? I can't imagine how anybody maintains 30-year-old software and makes large-scale changes like that without a type system. It's unimaginable.
Starting point is 00:52:34 So for me, the benefit of type systems is maintainability. Oh, and designability, right? So a type, I often write the types of my programs up front. I write the type,
Starting point is 00:52:45 you know, the data types are super perspicuous. You know, if I say, it's a bit like writing the classes of an orbit, of an orbit, obviously language, right? But if you have no types,
Starting point is 00:52:56 no classes, nothing, just, I don't know, S expressions. Types are my design language. They're the way that I start writing my designs. I'm trying to understand other mindset. Like, let's say C, for instance, where I remember a lot of stuff
Starting point is 00:53:13 when I was learning in in college, for instance, many times where I'd add a character to a pointer or something, and it just works because it interprets the character as a number. So is there any value to having that, kind of type system or is that just strictly unredeemable? Just you're stronger. I think there's no benefit to weaker. Just off the top of my head, one thing I think of is there is a set of programs that
Starting point is 00:53:45 will work but don't satisfy the type system. Yeah, that's why. But those are, I mean, I don't know if they're good. That's subjective. Oh, no, they may be good. So, so, so, so that like us, like we start. If you have Pascal and you write a function to reverse a list of integers, then if you apply it to a list of characters, the same machine instructions would work, but it is rejected. So we have rejected a perfectly decent program, bad.
Starting point is 00:54:16 Our goal is to expand the collection of the programs that satisfy the type system, to include as many as possible of the programs we want to run without including any of the bad programs. that we don't want to run. Okay? Parametric polymorphism is a big step in that direction. Other type system innovations are a big step in that direction. But there will always be some programs that would run perfectly well that the type system rejects. Imagine a tree that contains integers at every node.
Starting point is 00:55:00 But if you are 17 deep in the tree or 34 or 51, if you're in a multiple or 17 deep, the integers can be characters instead. Or the integers all turn out to be characters, right? Now, you could write a program that generated such trees and you could write a program that consumed such trees, knowing that every 17th layer we switch to characters. But most static type systems would make it pretty hard for you to accept that program. And yet it will run. Now, you might say, oh, but I really want to write that program, guys, you know, don't get in my way. Well, then we should provide a way for you to bail out into dynamic typing. Right?
Starting point is 00:55:49 What I'd like to do is to say, okay, so if all else fails, then at least you can, as it were, pair up a value with its type representation. was one reason we don't want to interpret an integer as a double-position float, for example, is that they don't even have the same representation, which is just nonsense. One possible way, which in untyped languages let you do, is to tag every integer and every double-position float with the fact, I'm an integer, I'm a double-position float, but that has a lot of overhead. So one merit, I think it's not the biggest single-barat, but it is a major merit of static systems, is you have no tags.
Starting point is 00:56:27 you know that if it says it's an integer, it's going to be an integer. You know that if it's a double position float, it's going to be double position float. But if you're not sure, maybe we could make a way to make a pair of a type representation and this value. The type representation is now like a runtime tag. It's like a little runtime data structure that describes the type. And then in your program, you could say, now I want to say, oh, I've got this type dynamic. We'll call this pair a value of type dynamic. Now, when I want to take a value of type dynamic
Starting point is 00:57:00 and treat it as a character, I want to say, ah, look, look at the type dynamic, see if it says it's a character, if it is, return the character. If not, crash, like runtime failure. That's fine, you can do that. And Haskell has good support for dynamic typing where necessary. So my story would be,
Starting point is 00:57:19 static typing should expand to carry as much as possible and where you absolutely cannot do it, then use dynamic typing and we provide facilities to support that. One thing I wanted to talk with you about is the compiler. How does the GHC work on a high level? So GHC takes a string like any other compiler,
Starting point is 00:57:41 the source code of the program, parses it, and then it type checks it, because is this a type correct program? Then it converts, converts it to LAMPT calculus. Now, the HUSCOL, the original source tree, has 50 different data types,
Starting point is 00:58:05 50 different kinds of nodes, some of which have 30 or 40 different variants. So it's a really big, diverse, complicated data structure, the AST. Lambda calculus has this variant of the Lambda calculus has eight. One of the data, or maybe two or three data taps with eight constructors. So it's like taking a gigantic language and squeezing it down into a tiny one.
Starting point is 00:58:36 And that tiny one we can then optimize. That's the optimizer works on that. So the front end does pars, rename, type check, D sugar into Lambda calculus. That's the front end. The Lambda calculus, particular language, is called GHC's core language. I'm quite proud of it, because it's been very, very stable. it's 35 years old and it has barely changed since birth. That's amazing, right?
Starting point is 00:59:03 Because Haskell has changed a lot, a lot. So almost all of the innovation in Haskell has been in the front end. Very little in core. Now, the back end, the core optimizer, has changed a lot too. But all of the changes that were useful there would have been useful 30 years ago, right? So there are two completely separable things. So I'm quite proud about that. Core, then we do a lot of core to core passes.
Starting point is 00:59:32 That simply take core programs, we'll use core programs, right? Lots and lots, long pipeline. Then we convert it to C-minus-minus, which is a prototypical imperative language. Think of it as a portable assembly code. Right? So that bit is meant to be platform-independent. So it's simply, that's the compiler that take...
Starting point is 00:59:54 Do you remember I said if you take Lambda calculator? and compile it, that's that step, right? I want to compile the Lambda calculus into machine instructions, really, but I don't really mean machine, I mean portable machine instructions. That's called C minus minus. Then I want to convert C minus minus
Starting point is 01:00:11 into actual machine instructions for various platforms. And there we could either do that directly with an native code backend or go via LLVM. Interesting. I've never heard of C minus minus. Why not just go directly to, I would have thought, maybe assembly or something like that? Well, because what happens, assembly for which processor, please.
Starting point is 01:00:30 Oh, I see. I guess it's the, I would have thought the Lambda calculus part was already portable. It is, yeah. You could go straight, but, but, so there's work to go from Lambda calculus. You could go all the way to X-86. Then throw all that away and now go from Lambda calculus to PowerPC. Oh, dear, I've just duplicated a lot of work. by going from Lambda calculus to C minus minus
Starting point is 01:00:55 and then from C minus minus to X80s, C minus minus, we've avoided duplicating the work that went from Lambda calculus to C minus minus, right? When you see that, that is pretty obvious, isn't it? You want to make the platform-specific bit as small as possible. You would like to have, as it were, like a generic architecture, one that can do addition and as a program counter
Starting point is 01:01:19 and a stack and so forth. That's all C minus minuses. It's just a portable assembly language. And then you say, oh, the nitty gritty of, you know, whether you have double precision add and set the floating point bid here and there. That, well, that's platform specific.
Starting point is 01:01:33 Core is itself statically typed. You know, but in some ways it's surprising because no other compiler has this property, no other production compiler. By core is statically typed. I don't just mean that the initial program was typed correct. I mean that a core program,
Starting point is 01:01:47 you can run a type checker on that. I might say, why do you need to? But after all, if GHC is correct, it started with a type correct call program, because it came from a type correct Haskell program, assuming the D Sugarer was right, and all the optimizations, if they're right, will generate a type correct call program. So why do you need to type check her? Answer, to discover bugs in GHC. Now, these are serious bugs, right?
Starting point is 01:02:10 If you ever take a type correct call program and an optimization pass produces a type incorrect call program, what will happen if we don't have the type checker for call we'll generate machine code and we'll run it and we'll get a segfault now we have to backtrack from a particular test program that now crashes all the way to back up the pipeline up the pipeline up the pipeline up the pipeline up the pipeline oh it was this pass of ghc that was faulty that's super hard to do because you know you're getting out gdb on some runtime failure that is an indirect and perhaps distant consequence
Starting point is 01:02:52 of the fact you just generated a type you just made a bug in the optimizer. So it is amazing to have a type checker for call. Now, why does nobody else do this? Well, it's because their intermediate language typically, and this I really am talking typically, because I know of no other compiler that has this property, none, production compiler.
Starting point is 01:03:16 Typically then, you know, complex syntax. trees decorated with all sorts of pragma information and things are hanging on it onto it here and there and you know there's no there's no type checker for it at all and there's no hope of one so i'm very proud of the fact that core is statically typed and i'm also also think it's a the most delightful thing is that the way that it is statically typed it is an implementation of something called system f So System F, when I said Lambda calculus, Lambda calculus as Alonzo Church had it, was untyped, had no typed system at all.
Starting point is 01:03:55 But Girard defines something called System F, which is a statically typed Lambda calculus, a rather powerful one. And Core is essentially System F. So we literally adopted something from the nerdy, theoretical computer science, you know, logic community, logic of mathematics community,
Starting point is 01:04:14 and adopted it directly in a production, implementation. So I'm very proud of that. I think core is, and the fact that we can do, we have 35 years worth of development that has been not just not impeded, but actively aided by statically tapped into medium language, which is really a big marker in the ground. Watching all your talks, reading everything, one of the interesting data points that you brought up was that Haskell is talked about more than used when you compared stackover
Starting point is 01:04:46 flow volume and actually GitHub volume, like who's actually using the programming language. Why do you think that is? So Haskell embodies one key idea. Immutability changes everything. There's a quote from Pat Helen's talk that I think you also paper, which I think you also looked at already. So it says programming with values changes everything about the way you think about programming. It's just mind changing.
Starting point is 01:05:15 It's not necessarily better, but it's not necessarily better. It is different. And so Haskell takes that idea and runs with it. Everything is driven by that one idea. Everything else is incidental. In the early days, that meant we just said, well, guys, suck it up. We'll keep changing the language. And if it breaks your program, it's too bad.
Starting point is 01:05:34 So it is a bit peculiar. And therefore, also it felt a bit academic, but initially it was really not very powerful. It was taking the key idea. But you couldn't do very much with it. We talked about that, right? So over time GHC and Haskill in general
Starting point is 01:05:47 has become more and more powerful. The type system has become less and less in your way and more and more useful. All the obstacles that make functional programming harder become better. The compiler generates faster code, it allows faster and so forth. So it has become
Starting point is 01:06:03 less, if you like, peculiar. So we've become more and more taking into account the needs of our users. But in a way, the sort of cultural heritage is we never give up on the one core principle. We're just not going to give you unrestricted
Starting point is 01:06:19 side effects. Sorry. You want to say unvoiced before my own and put up with the consequences. So we're going to stick to one core principle and then we'll do lots of work around the evidence to make that better. Right. So that does limit our community somewhat. It does mean you have to
Starting point is 01:06:35 you really have to think in a different way. Immutability changes everything. That means you'd have to think a different way about programming. Maybe you don't want to think in a different way. That's fine. then don't use Haskell, right? So in a way, we've started from a very small user community, a very sort of pure and nerdy one, and growing larger and larger, but all slowly,
Starting point is 01:06:54 but all the time maintaining faithfulness to this core principle. I thought that was very unique about Haskell because I feel a lot of the other programming languages are user-centric. I mean, if people want something, they work on it, they add it, Whereas Haskell feels more principled, or it's all starting from these ideas. And if you don't satisfy these ideas as a user, that's fine. For instance, in one of your talks you mentioned somewhere that there was a release of the compiler where if a file wasn't type correct, then the compiler would delete the file.
Starting point is 01:07:37 Oh, it reported the error message first. Yeah, I reports there but I thought that was absurd. I mean, very hostile I guess to the I mean, well, it's if you're type safe, no problems but. Oh, it was a mistake
Starting point is 01:07:51 right, it was a bug. It wasn't deliberate. And it only happened, you know, how did the bug get out? Because of course, if it always did that, we'd have noticed. How did it get into a release? Well, it was only on Windows and only when you compile a module that was not in the current
Starting point is 01:08:06 directory. But in that stage our users were very forgiving and somebody wrote to us and said, well, by the way, Simon, you might like to know that GHC does this, but hey, don't worry about it. You know, I just copy all my files somewhere else before I compile and then I copy them back. So, of course, those days are long gone.
Starting point is 01:08:23 We pay a lot more attention to our users and have much more rigorous CI testing than therefore we did. Right. So that's a story from a long time ago, but it's a good cultural story, but it suggests that we care about our users very much, but we care about users who in their hearts want to be principled. We're trying to appeal to. One of the things I like best about Haskell is people often say,
Starting point is 01:08:45 I just enjoy writing Askell. It's fun, right? My boss doesn't allow me to because, you know, somehow doesn't fit with my production shop. But for me, I would go for, I love writing this stuff every time, every time. That's very rewarding to me. There's also this interesting, I don't know if it's a cultural value, but it's a statement that you say often in the context of these older talks, that you say that you avoid success at all costs. Could you explain what you mean by that phrase?
Starting point is 01:09:21 Oh, yeah, this was just a little play on words. It was in a retrospector on Haskell. I gave an invited talk at Popple in a long time ago, probably 20 years ago. So it's a little play on words because you can read it as either avoid success at all costs. And that's what we've been discussing. Success at all costs means compromise your principles in order to satisfy your users or think that you're satisfying users. Give them what they say they want. Or if we build it, they will come kind of deal.
Starting point is 01:09:54 So avoid success at all costs. Or if you parenthesis it the other way, it says avoid success at all costs. and all costs avoid success. And that's saying, that's a little joke, but it says, if you're too successful and have too many users, it becomes more difficult to make changes. And we experience that right now. So I devote many, many more of my personal cycles
Starting point is 01:10:22 to backward compatibility issues than ever I did. I have devoted hundreds of hours and hours, well, days and days, weeks and weeks in the last year or two, to the following what seems to be a very simple property. If you can compile a program, a package in a whole program
Starting point is 01:10:44 with GHC 10.0 and we released GHC 10.2, you should be able to compile that same package unchanged with GHC 10.2. Seems reasonable, right? After all, 10.2 should just be better. But no, JTC has never
Starting point is 01:11:00 had that property. And making it have that property is turned out to be very, very time-consuming. Previously, we just never cared. Then we started to care but thought it was a lot of work, and now we're investing the work. I guess that was from a long time ago. I think nowadays, software engineering,
Starting point is 01:11:18 there's been a major shift in the last year where a lot of code is being generated by these models or these LLMs. How do you see programming language design shifting to accommodate a world where a lot of the code is no longer written by humans? I think it may be the best thing that's happened to statically type languages for a long time. Because, as we've been discussing, with a static type system, you cut down the space of programs that the LLLM can generate. Because it is perfectly capable of running the compiler and saying, oh, darn, that was a bad program, better fix it. So a zillion iterations get done behind the scenes.
Starting point is 01:12:00 was in an untyped language the first one it coughed up you'd have had to run or run against its test suite or who knows what but it's it drastically tightens up that cycle right so I think that statically typed languages are a huge boon
Starting point is 01:12:16 for LLMs because it's too easy to programs are just strings right we could they could just generate the next plausible word and you want you want to make any implausible programs programs that really shouldn't run
Starting point is 01:12:29 You want to make them not run right away. If there's a slider on, I guess, the strength of a type system and the other sides of the week, what do you see is the absolute strongest type systems among programming languages? Oh, Askel, I think. Askell is exploring the bleeding edge. There is an exception, which is that module systems
Starting point is 01:12:51 are a, and in particular sort of functor-style module systems, have explored much more detailed. in the ML O'Camel world. And in the Haskell world, we've essentially never gone there. But otherwise, I think Haskell's right up there. Now, of course, a language like Scala is also, as though Scala has almost everything Askell has, I think not quite,
Starting point is 01:13:19 but it also has subtyping and object orientation. So that's a lot more complicated, a lot more complicated, I think. and they pay a price for it. I think Martin Odescu would agree that they pay a price for it. So in complexity, it's probably poor complicated than Haskell's. And maybe in terms of power, so maybe I should have said to Haskell and Scala are the two leading. Haskell, Scalo, O'Cabell.
Starting point is 01:13:49 Perhaps I'll just put them in an equivalence class for now. They're not strictly comparable. They all have things which they're more powerful than the other probably. What do you think are the important problem? to solve in the future of programming languages today? Maybe, you know, what are the unsolved problems in the domain that are top of mind for you? I think it's actually hard to identify, you know, to say, here's a problem we want to solve. Let's try to solve it.
Starting point is 01:14:13 But I think another way to tackle is to say, what are interesting, you know, new languages out there that are exploring very different parts of the design space? And there, I think I do have a candidate. So the language that is my day job, right, I work for Epic, and we're designing a language. a programming language called Verse. Now, verse is a very exotic language. It's really a functional logic language. So it's yet more expressive than Haskell. It has an athletic type system,
Starting point is 01:14:42 but a very different one to Haskell's. So the way I think of it is like this, if you look at C and Fortran, they look pretty different if you're an imperative programmer. But if you look at them, if you zoom out, so you can see functional languages, then C and Fortran are pretty close together. you know, along with object-oriented languages,
Starting point is 01:15:01 they're all in a clump, right? And then there's some functional languages, you know, Haskell and ML, I know Camel and Scala out here. If you zoom out still further, then the imperative languages and functional languages are all together and verse is way out here. Right. So versus exploring a very new point in the design space.
Starting point is 01:15:19 But just like functional programming back in 1980, it seems sufficiently interesting and cool and unusual and weird that it's worth exploring, right? So back in 1980, nobody was said, we're definitely going to do functional programming and it's going to be useful practical applications. They said, that's pretty weird. You know, by all means, give it a try, guys. And that's kind of what I am with verse. One difference is that back in 1980, we were purely academics.
Starting point is 01:15:43 And now but verse is being developed by, well, epic games. So we've got some, you know, much more muscle behind it than, you know, was behind functional programming to begin with. So we'll see. It's a very interesting intellectual endeavor. adventure, I should say. Yeah, I think a lot of people, like students, they may be worried about AI or, you know, studying computer science.
Starting point is 01:16:06 Would you recommend people learn how to program today given that AI is starting to write reasonable code now? Oh, yeah. Yeah. So I think people have liked to be worried in the sense that I think there's going to be considerable dislocation. Right. If you were in the Industrial Revolution,
Starting point is 01:16:24 then lots of people lost their jobs as spinners and weavers and it wasn't easy for them to get a new job in the new economy. Now, the new economy had in the end had more jobs, but if you were one of the people who just lost their job,
Starting point is 01:16:37 that was not a happy place to be. From our perspective, Olympian perspective of a few hundred years later, we think, well, it's just a blip, right? If you're part of the blip, problem. So I think the right to be worried, we don't know how things will shake out. I'm actually optimistic that in the medium term,
Starting point is 01:16:56 if we don't destroy ourselves with some truly existential thing, but from an employment market point of view, I'm optimistic that in the end, you know, we'll just be in a higher place, that AIs will just be a bigger power tool. I mean, everyone, we like using compilers, right? We don't like machine code anymore. Compilers make us more productive.
Starting point is 01:17:16 Maybe LLMs can make us more productive. That's what I hope. I sort of believe modulo dislocation effects. Now, should we teach children or even undergraduates how to program? So I think still yes. The reason is because one way to say it is co-pilots and need pilots. I think co-pilot was quite a good title that Microsoft gave their tools, because it encourages you to believe it's your partner, not your boss.
Starting point is 01:17:49 if LLM spit out a pile of goop and we literally do not understand what it does but we just try it and it kind of works. That might be okay if we're just throwing up a quick visualization. It might be less okay if the quick visualization is going to drive our policy choices about as a nation whether to go into lockdown because of COVID or if this program is going to run my airplane
Starting point is 01:18:12 or train signaling system. So now those are extreme ends of the spectrum. from quick and dirty things, it really doesn't matter if it doesn't work when it kind of does a lot of the time. Absolutely fine. This is a 30-year code base that's going to last a long time.
Starting point is 01:18:30 I really want to make sure that it's... Like, putting new stuff into J.C, if somebody sends me a pile of AI-generated code to put into J-HC, I'm not going to put it in unless I've reviewed it or somebody's reviewed it because in 10 years' time, I'm going to want to change that code.
Starting point is 01:18:43 How do I even know what it does? If it's simply a magic incantation that somebody's done that kind of worked on, the test they did, but maybe won't work in deployment. That's no good. So I really want long-lived maintainable code to be well reviewed. Sorry. And to do that, I need reviewers who can write code. Let me mention one other perspective. If you think about what every child should know, when I think about whatever you like a child should know about computing, I would include
Starting point is 01:19:15 binary and bits. Just as for physics, I would include atoms and molecules. Now, it's not that in real life anybody manipulates atoms or molecules, or takes decisions which are based directly on knowledge of atoms, but somehow knowledge that all matter is made up
Starting point is 01:19:34 of atoms. You know, constitutive a finite number of elements at Atman's group, but that knowledge underpins everything we understand about the natural world. If you literally have never been told that, you are sort of inusculated, even as a citizen, let alone as a scientist. So, if you literally do not know that everything has composed the bits,
Starting point is 01:19:55 that words and music and text and LLMs and everything, it's all just bits, I think you're crippled. So I want every child to learn, you know, it's like I want you to learn the bottom. It's all bits, nothing else. It's all just bits. I even have a talk. The talk is called Bits with Soul. So easy to
Starting point is 01:20:18 Gret for. It's a talk I gave to an audience that was not computer science audience at all. It was a completely lay audience ranging from 14-year-olds to professors of quantum mechanics. Pretty difficult audience to address. It was meant to be all about codes and coding and bits. So it tries to get at the essence of
Starting point is 01:20:37 why do I think it's important that every child, every person, every human being should understand something about the computational universe that surrounds them That is founded in bits. Now, just to develop the analogy a bit further, I would then say, and it also, I think I want them to also know about programming, programs. I want them to know that computers fundamentally execute by following machine instructions blindly, right?
Starting point is 01:21:00 That is not magic, it's not hocus, focus, it's just remorseless and very dumb, right? It's incredibly empowering then. And also to learn the basics about how neural networks work. In the same talk, I explain how a one neuron neural network works. And that's enough. Then it's actually true to say, not distorting the answer to say, chat GPT is just a trillion of those wired together. And astonishingly, that very simple. So all chat GPT is is a trillion floating point numbers and a lot of floating point arithmetic.
Starting point is 01:21:37 That helps you to make sense of a question like, can chat be chat. HEDT have feelings. Well, it gives you, of course that's a philosophical question, but informs your, you know, your discussion about it. If you know that all it is, is trillion floats and a lot of floating point arithmetic. That's all.
Starting point is 01:22:01 Nothing more. Anyway, sorry, long answered your question. So, yes, basic programming, absolutely. Yeah. Becoming very skilled in how to use, you know, web Django framework 2.7, maybe not so much. Maybe.
Starting point is 01:22:18 I think one thing that LMs are very good at is knowing the arcane and complicated APIs that many of these frameworks present. There's just 10,000 functions you've got to know. And if an LLM is really good at knowing that, I don't want to learn. You mentioned somewhere, someone asked you, what's your favorite programming language?
Starting point is 01:22:38 And obviously Haskell's got to be number one. Yeah. But for your second favorite programming language, you said it was Excel. At that time, why was Excel your favorite programming language? Oh, because it's the world's most widely used functional programming language. The formula language is a functional language, isn't it? Doesn't have any side effects?
Starting point is 01:22:57 You program entirely with values. So the formula language of Excel is a functional programming language, a very weak one. It doesn't even let you define new functions. And it has a very limited collection of data types, namely just, you know, flat arrays and numbers and strings. So, when I was working for Microsoft, I took it as my, what's the word, Walcry, to say, let's take that idea.
Starting point is 01:23:28 Excel is the world's most widely used functional language by three orders of magnitude, and, oh, is the word most widely used programming language by three orders of magnitude? Not just functional language. Excel is used by many, many more programmers, users, domain experts, than any imperative language. Right?
Starting point is 01:23:52 Imperative language is a few million users. Excel, hundreds of millions of users, right? Even if you just restricted users who are using formulae. So how can we delight those users? Answer, take ideas from functional programming and use them to make Excel's formula language more powerful. It took me 20 years, but it Excel did finally add Lambda to Excel.
Starting point is 01:24:20 Look it up. There are blog posts about it and many YouTube videos about it. So you can now program in Excel using full Lambda as a Lodzo Church originally defined it. You can write anything like because Lambda is computationally complete. You can write any computation in Excel now. It would be a bit slow, but you can. And much more practically, you can take formulae that previously, that previously you just copy-pasted here and there,
Starting point is 01:24:48 and you wanted to make reusable, wrap them up in the Lambda, and now you can just call the Lambda. And now it is a proper grown-up functional language that is too incomplete. Just search for Lambda Excel. Those two keywords will get you lots of raw material. Last question for you is,
Starting point is 01:25:06 knowing everything that you know now from your career, if you could go back to when you just graduated from college and give yourself some advice, what would you say? all of these people who you see very successful wandering around looking as if they've made it I guess you might class me among them now they are all of them just making it up as they go along they feel insecure uncertain not sure what to do next not sure of what their next steps are not sure of what next big problem are going to tackle unsure about whether they're what doing is going to be successful or not and so all of their confidence is I mean they project confidence
Starting point is 01:25:44 maybe. That's partly a life skill but often they're not. And so the fact that in those days of course I felt very not confident, I would say, you know, since all of these successful people are making it up as they go along,
Starting point is 01:26:00 it's fine for you to be as well. And they've been lucky, moreover. They've been lucky. They've had but if you want to be lucky, you do need to put yourself in a position where accidents can happen to you.
Starting point is 01:26:17 And that means taking risks. So if you want to be lucky, you need to be in a put yourself in positions where lucky things could happen. And that means taking some kind of risk. So if you're very conservative, never take any risk. It's very unlikely that the accident that is life transforming will happen. That's a balance, of course. But it means accidents are not so bad.
Starting point is 01:26:39 And of course, I'm dying as bad, but lots of action. accidents may change your life in a way that might be surprising to you, but turns out to be not so bad in retrospect. Awesome. Well, yeah, thank you so much for your time. I really appreciate it, Professor Jones. We will and last talking to you. Thanks, Ryan.
Starting point is 01:26:56 Hey, thank you for watching this podcast. If you liked it and you want to see the show grow, please support with a comment or a like. Also, if you have any recommendations for people you want me to bring on, please drop a comment. And guests like Barbara Liskov, Mike Stonebreaker, Mark Brooker, these were all people that I brought on because someone left a comment. On another note, aside from the podcast, I'm working on building the ergonomic keyboard that I wish existed. Here's a glance at the prototype. It's a split keyboard. So there's two sides.
Starting point is 01:27:27 This is in the case. But yeah, we launched on Kickstarter and we hit our goal within eight hours of launching. I really appreciate it if you were one of the people who grabbed one of the early units. We're now working on the long journey of building the tooling now. And so if you still want to pick one up, I've left the late pledges open on Kickstarter. So you can grab one there. I'll put a link in the description. Thank you again for watching the podcast.
Starting point is 01:27:50 And I'll see you in the next episode.

There aren't comments yet for this episode. Click on any sentence in the transcript to leave a comment.