Functional Pivoting – Tackling Ruby, Haskell, and Rust Playgrounds with Rein Henrichs
Director of Developer Relations at New Relic, Jonan Scheffler, talks to Principal Software Engineer, Rein Henrichs, about working in Ruby and pivoting to Haskell, preferring functional programming, and transitioning to Rust, and gives some awesome resources and recommendations if you might be into the same sort of thing along your own way.
Should you find a burning need to share your thoughts or rants about the show, please spray them at [email protected]. While you're going to all the trouble of shipping us some bytes, please consider taking a moment to let us know what you'd like to hear on the show in the future. Despite the all-caps flaming you will receive in response, please know that we are sincerely interested in your feedback; we aim to appease. Follow us on the Twitters: @PolyglotShow.
Jonan Scheffler: Hello and welcome to Polyglot, proudly brought to you by New Relic's developer relations team, The Relicans. Polyglot is about software design. It's about looking beyond languages to the patterns and methods that we as developers use to do our best work. You can join us every week to hear from developers who have stories to share about what has worked for them and may have some opinions about how best to write quality software. We may not always agree, but we are certainly going to have fun, and we will always do our best to level up together. You can find the show notes for this episode and all of The Relicans podcasts on developer.newrelic.com/podcasts. Thank you so much for joining us. Enjoy the show.
Rein Henrichs: I'm good, thank you. How are you?
Jonan: I am also good. It's been a while since I've seen you. The last time I saw you we were on a podcast together on your podcast.
Rein: Yeah, so now we only do podcasts together. That's how our friendship works now.
Jonan: Which is good because our friendship has been somewhat inconvenient after you abandoned the Portland area and moved off into the wilds in Washington. But you're now back here.
Rein: Like 30 minutes away.
Jonan: It was ridiculous, people. I want everyone on this podcast to know that Rein is an abandoner. I miss you. And it was really nice getting a chance to talk to you again on your podcast. And now I am glad to be back here.
Rein: Thank you for inviting me on.
Jonan: We've known each other a long time, like more than ten years. I think you were one of the very first people I met in tech as I made my way into the industry when you were writing Ruby.
Rein: Yeah. Do people know about Ruby on Ales?
Jonan: If they don't, they should.
Rein: Well, it's not happening anymore, so there's that. But it was a regional Ruby conference with a focus on Ruby, obviously, but also responsible social drinking.
Jonan: [laughs] It was exactly that, yes. It was a chance to drink some really good beer in Bend, Oregon, which is famous for really good beer. And it was unique amongst conferences, even Ruby conferences, which tend to be very close-knit and community-oriented. The first one was held on couches. We were sitting on couches in a small movie theater.
Rein: Yeah, because it was in a movie theater. It wasn't in a conference center.
Jonan: Yeah, it's a beautiful conference. If you ever want advice on how to run a good community conference, look up Ruby on Ales. Find those organizers; they're smart. They had a really good MC a couple of years. If I remember, it was me. I was that.
Rein: Yeah. I mean, not all of their decisions were great.
Jonan: [laughs] So you wrote Ruby for a long time, but you didn't start in Ruby. What was your first programming language?
Rein: I actually started as a designer. So I guess my first programming languages were HTML and CSS. And we can fight in the comments about whether those are programming languages, but they are.
Jonan: Oh, that does sound lovely. I really would enjoy that. And then you learned Ruby from there.
Rein: Yeah, I needed to make stuff interactive. And I saw DHH's 15-minute blog video. And I thought, oh, okay, this is a thing I think I could understand because I had looked at PHP and gone, "I don't think this is for me."
Jonan: Yeah, this is very similar to my own path into Ruby. I looked at PHP because I was tired of typing the same thing into various HTML files. I would have the nav bar at the top, like, this is the about page. And then someone's like, "Can we call it contact us?" And I'm like, "We could, but now I have to go and change the HTML five times.”
Rein: And it wasn't like, oh, wow, PHP is bad. I had no frame of reference. It was this looks very complicated.
Jonan: Yeah, I was super confused. I had to install a LAMP inside of my computer? Very complicated. So you worked with Ruby for a long time, and then you got into Haskell. Was that the direct transition, or was it Ruby, something, Haskell?
Rein: So it's interesting because I actually got into Haskell around the time we met because I started Haskell in 2009 or 2010. And there is an International Conference on Functional Programming which is called the ICFP. And they do a challenge thing every year where you have to write a program that solves a problem. And the challenges are always super complex. And they're generally...the problem you're supposed to solve is a metaphor for some deeper problem. And so, in this case, the problem was in terms of designing fuel for an engine. But it was actually a research problem. Then this was just how they got it across was with this metaphor.
Jonan: You went into this ICFP challenge with Haskell and designed a fuel.
Rein: Yeah, so I was on a team. There were a few of us. And I was brand new to Haskell, so I didn't do very much. It ended up that the problem was about linear algebra. And it turned out that one of our teammates was a specialist in linear algebra, like that's what he did his Ph.D. on. But then he had appendicitis. He had to go to the hospital. But all I could really do was write scripts in Bash and Ruby that sort of glued stuff together. I didn't write a lot of Haskell, but I did learn a lot of Haskell.
Jonan: And so you picked up Haskell, and you quite enjoyed it. You think that you prefer functional programming.
Rein: I think it might be because that might be the way my brain works. I think my brain works in a more sort of inputs and outputs way and less in like, you're going to...so a thing that I couldn't really never wrap my head around was in C when you want to write to a buffer, you pass in the buffer, and then the function you're calling mutates the buffer, and then you keep using the buffer. And it took me a really long time to wrap my head around; why wouldn't you just have it return a buffer, and then you use the buffer?
Jonan: And so I want to pause for a second because I suspect there may be some people listening who are unfamiliar with what it means to be a functional program. But if you were to give us just a high-level overview, describe that thing.
Rein: Well, there are, I guess, as many different meanings of functional programming as there are functional programming languages. The thing I mean, in particular, is what's called a pure functional programming language, which means that functions can only take in inputs and return outputs. They can't mutate the world as they do it.
Jonan: This is, I think, exactly the piece that made it clear for me; as much as it is ever clear, I am a miserable Haskell programmer. But in my defense, I had a bad teacher. It was you. You were my teacher. Do you remember teaching me Haskell? I was just teasing.
Rein: A little bit. Yeah, I wasn't good at it then. So that might be why.
Jonan: [chuckles] No, actually, it was great. It was a really good introduction. So the idea that things are immutable. And this, of course, is very different than the world in Ruby, where everything is mutating everything all the time. I create a string, and that string is effectively set in stone, and I can pass it off to a function. But that function is giving me a new thing. It's not modifying what I've already got.
Rein: And the interesting thing about that is in Ruby, we like to use a lot of functional idioms like map. Map is one of the archetypal functional programming functions. And so, in Ruby, you map a function over an array, and you get back a new array with that function applied to each argument. So if you map +1 over the numbers from 1 to 10, you get back the numbers from 2 to 11, 1 becomes 2, 2 becomes three. And because Ruby is not a pure language, that map can do anything. That map can go mess with the end of the array, and then who knows what's going to happen? And it is incumbent on the programmer. Like, the programmer has an obligation to not do anything bad, whereas, in Haskell, that obligation is handled by the compiler.
Jonan: This is why Ruby is often described as a footgun.
Rein: In general, the ability to mutate things makes certain programs easier, perhaps, but it also opens up a lot of avenues for confusing yourself.
Jonan: This is, I think, what's hard for me to get is it takes me a lot longer to write code in something like Haskell. And I don't think it's just because I'm still quite bad at it. I think that even if I was proficient in both of those languages, I would be able to move more quickly in a dynamic language. Now having been using Haskell for a while, and lately, I guess, Rust, you think that's true, even for you with experience?
Rein: Well, I think that my entry into Rust was made easier by having experience with both functional and non-functional languages. So Rust has some elements of functional programming. It has the very expressive type system that you would expect from something like Haskell. Lisp is also a functional language. So there's a huge amount of variation here. But it has a Haskell-like type system while also allowing mutation.
But the thing that it does is it makes you specify specifically where you want mutation to be possible. And it also uses the type system to avoid a whole lot of errors that you would get with C, for example, if you're not extreme...again, you have an obligation as a C programmer to free things properly, and so on. And that's really hard to reason about. So let the compiler do it is the selling point of Rust. There are things that humans are good at, and there are things that computers are good at. Turns out humans are not good at reasoning about how a CPU functions.
Jonan: That makes sense to me. [laughs] So this thing was what? This specific thing where I'm telling a function in Rust how it can use the data that I'm handing it was very interesting to me early on. I was using Rust when they used to have the Rust meetups over at New Relic's office here. And I'm sure none of this applies anymore because it was pre 1.0. But they had specific syntax so that when I hand off a...let's say I make a string that says, "I am Jonan," and I hand it off to a function that's going to swap out my name for yours, and I tell it that it's allowed to do that. I mark that string as I create it or as I hand it off. I think as I hand it off, I tell it okay, you can change this once, but then no one else can change it and that kind of thing.
Rein: Yeah, although with Rust, it's a little bit complicated because it's not technically about mutation. It's technically about ownership. And ownership is a pretty nuanced concept. And it's interesting because ownership is critically important in C, but C gives you no tools to reason about ownership.
Jonan: And by ownership in this context, you mean who's responsible for that memory ultimately.
Rein: Right. Each value can have at any given time a single owner. And if you want some other function to use the value, you can either transfer ownership to that function, or you can let that function borrow the value. And ownership basically has to do with things like who decides when to free this?
Jonan: So I can pass something off to a function and then make that function the owner or hold on to ownership. And if I'm holding on to ownership, then it still is my responsibility when I get that data back.
Rein: Yeah. And so people talk a lot in Rust about fighting with the borrow checker, you know, the borrow checker keeps telling me I can't do this or that. And every time that happens, that's a bug in C that you probably didn't write.
Jonan: Every time that you fight with the borrow checker in Rust, there's a bug.
Rein: If you had written the equivalent program in C, there would have been an ownership-related bug.
Jonan: I see what you're saying. Okay. I want to talk about this fighting for a second here because I think you made an interesting comment to me about that, about how people talk about this kind of confrontational reality with their compiler, that they're battling the compiler when they're writing code. The compiler won't let me do this. But what the compiler is actually doing is helping you along the way. We have a friend in the compiler, ideally.
Rein: Yeah, and I think this idea that you have to fight with the compiler comes from a combination of not being used to a computer telling you you're wrong all the time.
Rein: And it has to do; also, I think, with the ergonomics of early compiled languages and especially their error messages really made it seem like the compiler was trying to ruin your day.
Jonan: Yeah, do you think that that's a problem that Rust is solving for? It has good error messages.
Rein: Well, I think that Rust benefited by a combination of a very expressive type system and people who care about ergonomics and error messages. So it has that Ruby philosophy of the language is there to help the programmer. The language is there to make the programmer happy combined with a type system that actually allows the language to do more to help the programmer.
Jonan: And you would compare that to Haskell how? How does Haskell handle that? Does Rust do a better or worse job as far as ergonomics go?
Rein: Haskell has the ability to have the compiler give you very fine-grained and informative error messages about what you did wrong. But the error messages, and this is mostly historical, the error messages were not designed to be ergonomic. So GHC, which is the de facto standard Haskell compiler, was not designed with ergonomics like this in mind. It was originally a research project. And if you're working in an extremely high-skilled, close-knit community, you don't need to design things for a beginner. There aren't any.
Jonan: So they were writing it for themselves and, from that perspective, achieved wild success because they wanted as much information as possible, and then they would mostly figure it out anyway. They could have just as easily reported a list of numbers because they would know what those numbers were.
Rein: So you'll get a 20 to 30-line error message, and somewhere in there, it'll tell you exactly what it is as long as you can decipher it.
Jonan: I've heard really good things about the Rust community as well. What do you think is different about Rust as a language that drives the community to be the welcoming place that I've heard it described to be?
Rein: I think it's maybe a chicken and egg thing. Like, is the language the way it is because of the people that were attracted to the project, or are the people that are attracted to the project the way they are because of the language design? And I think it's probably like a feedback system. We got lucky because the people that initially designed Rust cared about other people. And then that has led to a positively reinforcing feedback loop where people that care about other people like Rust. And then, when they work on Rust, they inject that caring about other people into the language.
Jonan: So we're talking about these type systems. And I think that I may have artificially introduced a dichotomy here where it is not that functional programming languages are necessarily strongly typed or that dynamic languages cannot be functional. These are describing two different concepts. And I think you can probably put it more elegantly than I can. The opposite of functional is non-functional.
Rein: Yeah, it's weird because object-oriented and functional don't describe quite clear points in the design space. There are a whole lot of arguments or ways to describe a language as being functional. And they're basically as many definitions of functional programming as there are functional languages. The way I think about it is that the minimum you need to be an object-oriented language versus a functional language is an object-oriented language makes it easiest to work with objects, and a functional language makes it easiest to work with functions.
Jonan: And it's not that a functional language doesn't have objects at all; it’s just that they're not the primary vehicle.
Rein: So, if you look at Ruby, for example, you have anonymous first-class functions, which is one of the hallmarks of a functional programming language. They're called procs or lambdas. Historically, they weren't as ergonomic to write, and they weren't interchangeable with methods. And so, you don't have first-class functionality across the entire language. There are places where you can only call a method. There are places where you can only call a lambda. And you can convert back and forth, but how you do that is pretty arcane, and no one does it.
If you look at the history of things like Rails, one of the things that happened is they made it easier to work. In later versions of Ruby, they made it easier to work with functions. So the keyword lambda was replaced with the arrow. And the arrow was closer to what the lambda calculus would look like if you were reading a math paper.
And then Rails did that symbol-to-proc hack. And that symbol-to-pro hack is basically an easy way to create blocks that call a method. So this streamlines the ability to use functions interchangeably almost with methods. But Ruby still has two separate classes of things. There are methods, and there are functions. And in a language like Haskell, everything is a function. So in Ruby, they say everything is an object; in Haskell, everything is a function.
Jonan: And so if I wanted to have a collection of behaviors in Haskell, how am I achieving that without the benefit of objects?
Rein: So, with an object-oriented language, when you have a class or a prototype, the object ties the state and the behavior together. In a functional language, you basically have your data types. And then generally, you do this by writing a file that contains a module. And in that module is a collection of identifiers that all live together, and so you can have your data types in there. You can have your functions in there. And when you want to work on functions relating to a certain data type, you generally import that module, and you get both the data type and its functions.
Jonan: So Rust uses a similar model but somewhat different than Haskell did around these types. How is it different?
Rein: Rust is object-oriented in the sense that you spend a lot of time calling methods on objects. But you can tell from the way that Rust implemented it where if you want to define a method, you define a function whose first parameter is self. You can tell that methods are actually syntactic sugar for functions. So x.foo and foo with the first parameter x are equivalent. But Rust gives you a nice syntax for calling methods and organizing them around types. And so you get the best of both worlds because you can also use functions in Rust as first-class things you pass around.
And I think that's one of the things that might be trickiest for people that come from a Ruby background to understand is that you can define a function from A to B, and then you can pass that function as the argument to map, for example, whereas in Ruby, you would have to define a block. You would have to use the symbol-to-proc trick to call a method or something like that.
So in Rust, you kind of get the best of both worlds. You get object orientation, and there's a lot of syntax around calling methods on objects. But you also get the functions can be used everywhere that you would expect a function. There's no artificial distinction between methods and some other kind of function.
Jonan: I see. So in Rust, when you're working with types, you have to explicitly tell Rust what sort of type you're working with, or you only tell it about the ownership portion. Do I need to tell it that I have an Int32 here?
Rein: Well, it depends. And the reason it depends is because there's something called type inference. And type inference is basically the compiler's ability to logically infer what a type must be based on other places it's been declared. So if I say let x=2, and then I call a function that takes an i32, and I pass it x, well, Rust can infer, well, x must be an i32 in order to be used here logically. There's an implication that it has to have this type based on how it's used. And so what type inference lets you do is write the programs and hide the type declarations and have the computer figure them out for you.
Jonan: But even thereafter, they never appear in the code. So if I come to read it as another person and I'm trying to figure out what it is, how would I even get that information? How would I know?
Rein: So they are specified at some point, and it stores a logical chain of inference from a place where the type is specified to a place where it's used, and that's how the compiler figures it out. What Rust did is really interesting. The IDEs VS Code and IntelliJ and things like that will annotate the file with the types of things in line without editing the file. It's just a visual overlay effectively. But it will show you the inferred types as you go. And this is incredible. I wish Haskell had done something like this.
Jonan: That is kind of brilliant. But of course, then I have to use an IDE instead of my Vim that I love so much.
Jonan: Yeah. And if you enable legacy mode or historical mode, there's like an old people button that you click on, and it functions for Vim.
Rein: So I wanted to maybe talk a little bit about type inference again because there's a thing that I want to tease out here, which is that with type inference, you write the program, and the computer infers the types, right?
Rein: Why does it have to be this way? What if you told the computer what type of function you wanted and the computer figured out how to write it for you? Why can't we do that?
Jonan: Because it's confusing to me that that's a thing that's even possible because it sounds to me like GitHub Copilot, what you just described. I'm going to tell some types, so I'm going to say, "Hey, computer, Int32," then it's going to write a program.
Rein: But the thing about GitHub Copilot is it’s not based on the semantics of your program. It's not based on the meaning of your program. It's based on the letters you typed. And so when GitHub Copilot gives you back the definition of your function, there's no guarantee that it even type checks.
If a computer could infer the program, it could ensure that the program you get makes sense in the context of the type you're telling it to implement. But this really only makes sense if the type system is sufficiently powerful and expressive to narrow down the range of possible implementations to something close to the one you're looking for, either that or if there is a way to search through the space of potential implementations. Because otherwise, the computer will just say, "Well, this type could be implemented in one of a million different ways, and I don't know what to do."
Jonan: So I want to try and come up with a more concrete example because, frankly, I'm trying to confirm my understanding here, and I'm having a hard time getting a toehold. But what you're suggesting is that rather than type inference where because one time eight calls from now that function takes an Int32 from whatever this thing is where I am now, the Rust compiler is able to say, "Okay, that must be an Int32." And it chains the whole thing together, and we get annotations in VS Code. Rather than that world, when I start out, I say this is an Int32. And then I later hand it to a function with another Int32. And the compiler is able to suggest or predict an implementation for what that function does based on the fact that it's taking two integers of this size.
Rein: Yes. If you look at, let's say, the function add that adds two integers, the type for add is int, and it takes an int and an int, and that returns an int. Well, that could be literally any binary operation on integers. There's no way for the compiler to know that what you meant was addition because the type doesn't give the compiler enough help. So this is about you helping the compiler help you.
And what you really need here is the ability to write very precise types. You need something that's called dependent typing. And a dependent type system is a type system where the types can depend on the values. And so, for example, one of the types you can have in a dependently typed programming language is an array or a list annotated with this length. So this is usually called a vector. So you'd have the type vector of 5 ints or the type vector of 10 strings.
And now, if you want to write concat for two lists...and concat is to list as addition is to integers. So what you would say for vectors is I have a vector of length N and a vector of length M. What I need to get back is a vector of length N+M. Well, now there aren't a lot of things that you can write to fit in that slot. There are basically two. And so, this is a search space that's small enough for the compiler to assist you in finding the implementation. And there are languages like Idris and Agda where it literally will do this.
Jonan: So the tube, in this case being that I am taking my first factor and adding the second one on the end or vice versa.
Rein: What the compiler can basically say is, there are two implementations, one is Xs+Ys, and the other is Ys+Xs. It doesn't know which one comes first, but if it gives you those two options, and you pick one, then you're done.
Jonan: And there are languages that do this. You mentioned a couple. What are those?
Rein: There's a language called Agda, A-G-D-A. And this is an older, dependently typed programming language with a really robust history in programming language theory and research. And there's another newer one called Idris, which is a bit closer to Haskell in terms of syntax with similar program synthesis like have the compiler search through the space of things you're trying to do and pick one, help you pick one sort of capabilities.
One of the ways it does this is through a goal-directed search. So if I start with my input types and my output type, then I might say that I have an intermediate goal. I'm trying to get from A to C. And if I know a point in between B, then the compiler will help me get to B and then help me get from B to C. So this can help divide and conquer the problem.
And so, a good example of this let's take the vector concatenation. If I have my vector Xs and my vector Ys and I want to concatenate them, the things I have are Xs which is a vector of length N and Y, which is a vector of length M. And my goal is a vector of length N+M. If I in my program type Xs+?, now I have a new goal. Now I need to fit something in that hole that is a vector of length M. Well, I have one, it's Ys, and the computer knows to put it there.
Jonan: I see. Agda and Idris as these kinds of research languages. Is there one that is in common use that you can use as an example of something like this? Like something that is widespread outside of university circles even?
Rein: No. Next question. [laughs]
Jonan: It's not. This is a relatively...it's not a new concept. It's one that's been around for a while, but it's --
Rein: Yeah. It just hasn't really penetrated the practicing programmer demographic. I think Agda is a bit too quirky to ever really get there. But Idris was designed to try to bring dependent typed programming to the masses. So it is relatively easier to get a handle on because there is less syntax. There are less features.
One of the features in Agda is what's called mixfix notation. So if I have the operator + and I say X+Y, that's an infix operator comes between its arguments. With Agda, I can define an operator that puts its arguments anywhere it wants. So I can define an operator in Agda that's A arrow, B double arrow, C. That's one identifier with three holes for arguments, one at the front, one in the middle, and one at the end. And so with Agda, what this basically lets you do is define your own syntax.
Rein: But that's pretty hard to wrap your head around. And things like that are incredibly powerful but also hard to wrap your head around, I think are...Like Agda has a really powerful module system, but it's also really hard to wrap your head around. So I think that Agda is too sophisticated. Not that programmers are dumb, we're not. But we're trying to introduce people to a new concept that's pretty sophisticated. And I think Agda throws too much at people too quickly.
Jonan: You're metaprogramming in types, so you get to create your own. And it's not just like defining a type, but you're defining a --
Rein: Yeah. And so because types can depend on values, you can have a type for a sorted list, for example.
Jonan: Interesting. Dependently typed language is what this is called.
Rein: Yep. There's an Idris book that is really good. It's called Type-Driven Development with Idris. And type-riven development is basically this thing I'm talking about where you write the types, and then you ask the compiler to help you write the program based on the types. You say, "Here are the pieces I have. Here's the thing I'm trying to build. How can I get closer? Oh, this problem has five steps, and I know the second one. How do I get to it, and then where do I go from there?"
Jonan: Interesting. It's doing some fill-in-the-blanks for you or at least limiting your number of options, which I can see the value in that. From that perspective, being presented with fewer options, it makes my life easier.
Rein: The blank page syndrome is what's really hard for me in programming. I have to write a new function. Okay, I have an opening brace and a closing brace. Now what? When you use type-driven development, what you have is I have a thing that is shaped like this, and I have a thing that is shaped like this. And I want a thing that's shaped like this. Well, I also have this function that takes a thing shaped like this and turns it into a thing shaped like this. And if I plug all of those things together, oh, that's my program.
Jonan: And that is our program, Rein. We have finished our podcast. I liked the part where you recommended the book. Recommend some more books for the aspiring Reins of the future, people who listen to this and are fascinated by this level of thought around programming languages and design. I think that there are plenty of people trying to find their way through this, and it's difficult to find good resources, certainly.
Rein: Yeah, I have a really good one. So if you like Type-Driven Development with Idris, and you want to dig into more details of how functional languages work under the hood, if you want to pop the hood and pull out the carburetor and take it apart and see what it's made of, the book was written by Simon Peyton Jones who was one of the original designers of Haskell. It's called Implementing Functional Languages: A Tutorial. And you can download it from Microsoft Research.
And if you want to understand things like how do you implement type inference, if you want to understand things like how do you make functional languages fast when they create this incredible amount of garbage all the time, if you want to understand things like how do you actually evaluate a program in a functional language...program evaluation in a functional language is not like Assembly. Our basic understanding of how computers work at a low level doesn't map onto what functional languages do. Of course, it has to at some level because they have to be implemented on CPUs. But the bit that does that is basically the most complex part of all. How do you take the semantics of program evaluation in a functional language and make a CPU do it in real life?
Jonan: This is all very interesting to me. Thank you so much, Rein. I really appreciate you coming on the show to walk us through these. So if you were starting out again and you're looking for a bit of advice for someone who is an aspiring language geek...there are a lot of people I think (I hope that you would apply this label to yourself.) that get into software and dive so deep into languages that they just become fascinated by the intricacies of designing and implementing them, like Matz describes himself this way. So the aspiring language geek amongst us, where do you think they should be focusing their energies right now? Or what would you recommend to yourself if you had just been starting out?
Rein: I would say Rust is a great playground for understanding languages. I still think that Haskell has a lot of relevance. What made it possible for me to figure this stuff out was having good mentors. So maybe look for functional programming meetups in your area.
There used to be a really great Haskell channel on Freenode. Who knows what's up with that anymore? But I definitely wouldn't be anywhere close to where I am today without having mentors who...the way it was put to me once is; I’ll tell you this stuff for free, but only if you promise to tell other people this stuff for free.
Jonan: And that's how we build a whole world of people who learn to program.
Rein: One more reference, one more reference. So my what if the computer could help us write programs was a worse version of Conor McBride's talk: What Are Types For, or Are They Only Against? And Conor also has the best paper titles in the universe one is ‘Jokers on the Left, Clowns on the Right’. One is called ‘Doo Bee Doo Bee Do’. That is the title of a functional programming paper.
Jonan: [laughs] Conor McBride. I will look him up. All right. I think we should call it, Rein. It's been really good to see you. I could just hang out and talk with you for another hour about languages. You always blow my mind when it comes to this stuff. But we should eventually wrap up a podcast. Any parting thoughts for our friends?
Rein: Yeah. Well, thank you so much for having me. I haven't had as many opportunities to geek out about languages lately. So this was really fun.
Jonan: Thanks for coming on the show.
Thank you so much for joining us. We really appreciate it. You can find the show notes for this episode along with all of the rest of The Relicans podcasts on therelicans.com. In fact, most anything The Relicans get up to online will be on that site. We'll see you next week. Take care.