Jerod Santo:

Well, today I'm joined by Richard Feldman from Roc, the programming language Roc. That's R-O-C. Richard, when we first met, you were rocking Elm. You were knee-deep in Elm. I assume there's some sort of lineage involvement. Roc is your new - I'll call it new; new-ish - language. Is it inspired by Elm? Is it based off of your love of Elm? Do you still love Elm? Tell me the story.

Richard Feldman:

Yes. Yes, yes, and yes. So Roc is -- I say it's a direct descendant of Elm. It came out of my love for Elm, but also wanting to do different things. So Elm, for those who don't know, is a language that compiles to JavaScript. I've given a ton of Elm talks over the years. If I were still doing frontend development, that would be the first thing that I would reach for if I were doing big, complicated web apps. These days I'm doing Rust at Zed, so I've kind of gotten out of the web dev game... But yeah, I mean, Elm is a wonderful programming language, it's got a really great design, it's got a really great compiler... And the original motivation for creating Roc was "But all it does is frontend web development, and there's so much more to programming." I wanted something where I could get an experience that, where I have a language that's very simple, very nice, really focused on ergonomics... It's funny, because now I work in Rust all day and people talk about "Oh, Rust has such great error messages." And a lot of people don't know where that came from. But if you read the blog post where they originally introduced "Hey, we're redoing all the error messages."

Jerod Santo:

Elm, right?

Richard Feldman:

Yeah, they cite Elm as "We want to try and be Elm." And Elm, for me, is still the gold standard of the nicest compiler error messages, the friendliest compiler. And that's a really strong value for Roc, too. Except that Roc is for, I to say the long tail of domains, so not just servers. That's kind of the big one, that people always talk about... But also command line apps, native GUI apps, even really esoteric stuff extensions for code editors, or robotics... People have made a clock, a physical clock that has these flaps that go up and down, and they used Roc to program the logic for changing the flaps to show different numbers. Yeah, the goal is to make a language that's intentionally usable for lots and lots of different things.

Jerod Santo:

Right. I think Elm will go down in history as a super-niche... I know it's still around. I'm speaking as if it's a postmortem, but... You know, on its Wikipedia page, it changed the world, in a really good way. And I think it did break ground with regards to - well, compiler DX. I'm not sure what you call it, but really caring about the ergonomics and the experience of using the compiler... And it was game changing, and then everyone's "Oh, we should totally do that." It's like, "Yes, please steal these ideas", because Elm has a lot of stuff figured out that many people weren't thinking about back then.

Richard Feldman:

Yeah, it reminds me of -- I want to make sure I get the right band name. I think it's the Velvet Underground...

Jerod Santo:

Yeah.

Richard Feldman:

It's this band where it's -- it's not that Velvet Underground is this top-selling artist of all time, but rather that a million different bands that were really, really successful, and were top-selling artists pointed to them as inspiration for this or that aspect of their music. Yeah, I think we're sort of like -- there was a moment where Elm's rise in popularity was like "Oh, maybe this is going to take over the world." I think at this point we can be like "No." I think the idea that Elm was going to take over the world is definitely a past tense idea at this.

Jerod Santo:

Yeah...

Richard Feldman:

Still lots of people very happily using it who are doing web development... I'm not really plugged into the community anymore, just because I'm not doing that type of work anymore. But it's definitely -- this is a solid niche with its own community of happy people, but I think it's safe to assume that it's going to stay niche. It's not going to take over. It's not going to replace TypeScript.

Jerod Santo:

Right. Yeah, there's even a saying to that phenomenon, the Velvet Underground phenomenon... In sports it's called "Your favorite player's favorite player."

Richard Feldman:

Ah, there you go.

Jerod Santo:

It's the person who -- it's kind of like the actor's actor, where there's certain actors who aren't A-listers, but they just have the respect of all the acting community, because they're so good at what they do, and they never made it into stardom, but they're just high-quality, and solid, and they deliver in all these different ways that everybody respects them. And there's baseball players that are the same way... There's probably bands that are the same way, like Velvet Underground... And then there's programming languages where it's like "You know what? This is a language that your favorite language's author really respects." And that's cool.

Richard Feldman:

And even if you're not going to use it personally, you should go study it, because you should go learn how to do this thing really, really well.

Jerod Santo:

Right. And so you were not the creator of Elm...

Richard Feldman:

Oh, no.

Jerod Santo:

Evan. I can never say Evan's last name. How do you say his last name?

Richard Feldman:

So he pronounces it Czaplicki. We can go on a microtangent about this... When I first met him, we sat down at a cafe in San Francisco and I was like "Hey, so how do you say your last name?" and he was like "I'm not sure." And I was like -- I didn't expect to answer.

Jerod Santo:

"How old are you?" \[laughs\]

Richard Feldman:

\[00:08:12.28\] Well, and he explained that when he grew up, everybody said Czaplicki. But then he had very recently, just by coincidence of timing, been to a conference... I think it was in Poland. And I guess it's a Polish last name. And apparently, they would say it like Czaplicki. And he was like "So I'm not sure anymore, because the way I've been saying it my whole life is apparently not the correct way, so..."

Jerod Santo:

Yeah. "My life is a lie."

Richard Feldman:

But I think he still goes with -- yeah. Actually, there's a conference talk of his where he starts off by introducing it, and he goes like "Hi, I'm Evan Czaplicki. You might say it differently...", but then he goes on -- like, it's a conference in Europe... And I was like "Oh, I know what he's talking about there."

Jerod Santo:

That's funny. Brilliant guy, Evan. Like I said, game-changing programming language and runtime or environment for frontend development... But you were the evangelist, for a certain extent. You just fell in love with it, your business used it in production etc. and so you very much became a mouthpiece or a promoter of Elm. And now that you're not doing frontend as much, Roc is your thing, and so I guess they've lost kind of a promoter in that sense... But was the idea like "I'm going to now put all my weight behind Roc", and is it going to be the next big thing that's going to take over? Or is it going to be your favorite programmer's favorite programming language? What was the idea there?

Richard Feldman:

Well, so as far as my aspirations for Roc - I mean, pretty directly, this is a language where I'm like "The goal is to use it in industry, and have it be a successful language." And I would measure success by people are actually using it at their jobs, and are loving it, and are getting stuff done with it. As far as maximum popularity, it's like, sky's the limit. If this ends up being a top 10 or a top one most popular programming language in the world, great. But my focus is like, I'm not trying to get there by hyping it, but rather I'm trying to get there by making something that is just really great, that people love using. But the aspiration is pretty clear. It's like, this is not a hobby project. This is not just for fun. No, we want to make something that's really, really high quality, and that people really love using.

Jerod Santo:

Okay. So how far are you along that path to total world domination? Do you have people using it in production? I'm sure you're using it in some sense...

Richard Feldman:

So I guess you could say that we're using it on the Roc website...

Jerod Santo:

But not working for Zed. You haven't convinced Nathan that Roc is the way, the future.

Richard Feldman:

Hm... I have to be careful what I say here. We're not using it at Zed. I can say that.

Jerod Santo:

Okay. Fair.

Richard Feldman:

But I think there is a distinct possibility that either Zed or something like Zed could find a good use for Roc when it's ready. So yes, how far along are we on this journey - there actually are people using Roc in production right now. Very, very small group. But we've sort of tried to actively discourage that, just because we know that the language is just not at the level of robustness where I would personally be like "Oh yeah, go out and use it." The person who is using it has actually done -- it's a consultant who's done a number of different projects with it, and is happy with it, and still using it... But we did recently decide to undergo a rewrite of the compiler. The compiler is about five years old. We've learned a lot about the implementation, there's a lot of things we want to do differently, and we basically sat down at some point and we're like "Okay, what projects do we want to do next?" We have a bunch of different contributors to the compiler. And one person was like "Okay, we need to rewrite this part for this goal", and the other person was like "Oh, we need to rewrite this part, for this goal." And eventually, we sat down and we're like "Wait, so we're going to rewrite 90% of the compiler? Why don't we just start fresh and just get where we want to go, and then finally have a numbered release?" Because in order to communicate "Hey, we're still a work in progress, things are still changing", we've intentionally held off, and so far, even though we're 30,000 commits in and a whole bunch of GitHub stars and downloads and people trying it out, we still intentionally do not have any numbered release. We've never said "Here's version 0.1.0." So that's the milestone that we're working towards next, is like, with the rewritten compiler, that's going to be 0.1.0. That'll be our first numbered release. And we're looking to do that probably not by the end of 2025, probably sometime in 2026. \[00:12:17.27\] The milestone we're working towards now is we want to have the new compiler able to be usable for Advent of Code 2025, which means it's not feature complete, or at feature parity with the old compiler, but it is at the point where people can actually try it out and get some value out of it. So that's what we're working towards right now.

Jerod Santo:

Very cool. Well, one thing I do when I check out new languages is I like to go -- first of all, I'll do the little playground, of course. And you have -- Roc compiles to Wasm, so it's just sitting there, in the browser... So that's really cool. You can just start typing, hitting Enter, and you've got a REPL right there on the homepage. And then I go to the FAQs, because I love to see kind of the hot takes or the spiciness... Because a lot of times you're answering what people are asking, like, what they consider to be bugs, but you consider features, or whatever it is.

Richard Feldman:

Sure, sure.

Jerod Santo:

"Why doesn't it do this? Why did you make this design decision?" And you have a really nice FAQ right there on the website... And so many things that you say no to, in this really intentional way. Like "We have no plans of ever doing this." For instance, a maybe type, or an optional type. No plans of Roc self-hosting... It's written in two languages, to a certain extent, and that's the plan for now... And you're just very straightforward with like "No, we're not going to do that." And I'm curious -- maybe share some of those strong opinions and where they came from. Because you seem like you really know what you want in this programming language.

Richard Feldman:

Yeah, for sure. And I think the design of the language has evolved. Like, it started off being so similar to Elm that actually instead of writing a tutorial on like "Here's how to program Roc", there was this document, which actually I think is still in the repo, maybe, that was just called "Roc for Elm programmers", and it was like "Look, here's what's different." And that was it.

Jerod Santo:

It's a short list, right?

Richard Feldman:

It was back then, but now it's evolved so much that Roc very much has a very distinct identity. And I think if you look at Roc code and Elm code side by side, I think you would see some structural similarities. For example, the standard library, the API design in the standard library, I think Evan did an outstanding job with API design. That's one of the things he's really, really great at. I guess underrated at. People talk about the compiler DX and nice error messages and stuff, but... Yeah, he's really, really good when it comes to designing simple APIs, that are also very good at being reliable, and help you out with edge cases and stuff that. I think the API design, you would see a lot of Elm, a very, very strong Elm influence in those simple, but reliable APIs... But if you look at the code on an individual level, one of the most striking things that's different is that Roc now has a syntax that looks a lot more mainstream. There's a variety of reasons for this, but a really, really simple example of this is if you look at the old homepage - and actually, I guess the current homepage still has a little bit of this. In Elm, if you're like "I want to..." Let's see, what's a good example...? I want to do a -- this is a functional programming language, so you would do like a map over a list, and say like... I've got a bunch of names, and I want to uppercase all the names. So in Elm, you would say \[unintelligible 00:15:13.16\] That means "Go to the list module, get the map function", space, then the transformation function you want to put in there, like capitalize them, or whatever; a little anonymous lambda, or something like that.

Jerod Santo:

Sure.

Richard Feldman:

And then space, and then the last argument would be the actual list that you want to map over. Roc, it looks pretty much -- almost identical to what you would write in TypeScript or something, except the lambda syntax is slightly different... But you'd say \[unintelligible 00:15:41.13\] because the name of the variable, .map, where it looks kind of like a method call... Roc is not object-oriented, but the syntax in the new compiler does give you that sort of visual appearance, of like method style calls, with parentheses around the arguments, just like you would see in most languages, commas separating the arguments... So it visually now looks a lot different from Elm, even though to me the much more important thing is sort of the semantics under the hood. And that part feels a lot more like Elm. \[00:16:13.10\] So yeah, I mean, depending on your perspective... One thing I've learned when we made that syntax transition is that -- syntax is, obviously, a very polarizing thing. Some people look at that and they say "Oh no, this doesn't feel like Elm, which I love." And obviously, I'm a huge fan of Elm. I'm not trying to do something different for the sake of wanting to move away from something that I love. But at the same time, there's also a lot of other people who are like "Oh, I will actually consider this language now." For a lot of people, syntax being a lot different from what they're used to is just an absolute deal breaker. And actually, syntax was not the main motivation behind this. It actually was a semantic thing, which I'm happy to nerd out about if you're interested in that... But it was pretty clear that if we wanted to get the semantics that we wanted, we had to make the syntactic change, too. It just would not work with the old syntax. So from my perspective, it's sort of a moot point. I'm just not that attached to the syntax, so much as I am the semantics. And in order to get that, we sort of needed to make the change... But if someone is getting their first impression of the language, it's now gonna be quite different than the first impression they would get from Elm, just because the syntax is so much more mainstream-looking.

Jerod Santo:

So hovering on that - and I'm fine with getting into the semantics, because it sounds like you want to, and let's go there...

Richard Feldman:

Sure.

Jerod Santo:

When I'm thinking about the difference there, the one being some sort of type or module .map, and then you're passing in the actual list, as well as a function to map over it... That's one thing. But then you mentioned that now Roc has this lowercase list .map where the list is a variable that holds the list itself, and now you're calling map, and you're passing it - what? Just the function? Do you still have to pass a collection? How does it work?

Richard Feldman:

No, you just pass the function, yeah. \[unintelligible 00:17:52.08\]

Jerod Santo:

So that does feel object-oriented to me, where it's like "Do this map on me. I'm a list, do it on me. I'm an object of collection type", or something. How does that work?

Richard Feldman:

Yeah, so the feature name is static dispatch, and the basic idea is this. So in JavaScript, what's actually happening there -- so JavaScript is object-oriented, with prototypal inheritance. So what that means is you have an object, let's say it's named list... Maybe I should choose a different name for that for our example. Let's say it's called numbers, so it's clear that we're talking about a variable here. So you have numbers... Well, that's not good if they're strings. Let's call it names.

Jerod Santo:

There you go.

Richard Feldman:

Okay. You have names, and they're all strings, and you want to uppercase the names. Okay. So names is the name of our object in JavaScript. When you call names.map, what's happening there is it's going up at runtime up the prototype chain, it's looking at like "There's some runtime information inside names", and it's like "Oh, names has a prototype." And it keeps looking at the prototype until one of those classes in the prototype has an actual map function on it, or a map method on it. And then it says "Oh, I've found one. Great. I'm going to call that one, passing in the one argument", which is the function that is going to do the mapping operation... And then inside that method, there will be a sort of "this", that's automatically bound based on the variable itself, and then it's going to sort of do its magic. That's how it works in JavaScript, and in object-oriented languages usually you don't have prototypal inheritance, you have classical inheritance, with formally defined upfront classes, and stuff like that. And JavaScript does support that now, and so on and so forth. And Roc is totally different. It looks the same, but what's going on under the hood is way, way simpler. So Roc is a functional language, and one of the most important values of functional languages is everything's done with functions. We have plenty of functions. We don't have a first-class concept of classes, or methods, or any of that. So the way it works in Roc is super-simple. It's like, okay, I have this thing called names... Names has a particular type. The compiler knows what that type is, because we're a statically type-checked language, and we have type inference, so you don't need to annotate any of your types, actually, if you don't want to. You can have what I like to call 100% type inference, where all type annotations are completely optional. You can just never annotate anything if you want. It'll be totally fine, everything will still work. But the compiler can always figure it out. So it figures out "Oh, I see that names is a list." That's like the data type we call, just like Python, where it's like, you have the square brackets - that's a list in Roc. JavaScript calls it an array, Python calls it a list, Roc calls it a list... \[00:20:17.22\] So I say, "Okay, the compiler has figured out this is a list." Well, that list type is defined in some module, somewhere. Some module declares "This is what a list is." So the compiler says "Okay, names is a list. Let me go look up the list module." And they say, "Okay, does that list module expose a function named map? If so, great. Call it." Passing the names as the first argument to that function, and then whatever other arguments you gave to it get passed as the other arguments. So inside the list module, you have a function named map. The first argument is the list to map over, and the second argument is the function, and that's it. So it's a really, really simple way to say "I want to just take this thing and just call some function on it without actually having to declare what module that function lives in." That's all sort of inferred by the compiler. But there's no inheritance. There's no prototypes, there's no classes, there's no any of that. It's just plain old, completely ordinary functions in modules, and then you just call them in a syntax that looks similar to OO methods, but it's really just the compiler being like "Here's how I decide what function to call." That's static dispatch.

Jerod Santo:

Gotcha. So in layman's terms, it really is just kind of reorganizing the order of calls, and it's like "Here comes a list. Does the list module have a map function? Yes. Call the list module's map function with this list as the first argument, and then take whatever they said and call the second argument."

Richard Feldman:

Yeah. And if you want, you can literally call it in Elm style, if you want. You can say \[unintelligible 00:21:51.11\] .map, passing names as the first argument, and the function as the second argument, does exactly the same thing. The first way is just a shortcut for that. Now, the semantic thing that we wanted to get out with that is that here's the really cool thing that that gets you. It's that now let's say that I have a convention of having a function in your module named equals. And you want to define that to be, you know, equals. Like, it does the equals thing. So now we can make double equals the operator, just desugar, to like if you have A double-equals B, that just desugars to \[unintelligible 00:22:24.14\] And that's it. And so now we have the concept of just custom equality for everything. And how do you make custom equality? It's like literally, when you're defining your new type in your module, just write a function named equals, that takes two of these things, and then that's it. There's no restrictions on "Oh, equals means it has to return a boolean, and this and that." No, it just sort of works by the fact that the way that you want equals to work is everybody is going to sort of use equals in the obvious way, but there doesn't have to be this really formal declaration of "Oh, equals is a trait." Ad if you want, you can do a type alias. You don't have to write it out all the time. But all of this is just like, that one simple semantics for the dot means that you get stuff custom equals, you can also do operator overloading, where A+B desugars to \[unintelligible 00:23:18.24\] and now - great. If you ever want to do custom plus, just implement a function named P-L-U-S, and expose it, and you're done.

Jerod Santo:

Interesting.

Richard Feldman:

So yeah, it's a super-simple design that unlocks all these things that in most languages require a lot more formality and a lot more -- like, different language concepts. And it's really easy to teach. Like, you just learned 100% of what there is to that feature. That's all there is to it. But it just gets you all these things, and yet it's still just all functions. One of the things that always annoyed me a little bit about -- I'm going to use Rust as an example, because I like Rust. I use it all day at Zed. In Rust, you have this sort of trait hierarchy. You can also customize equals in Rust, but you have to say \[unintelligible 00:24:00.11\] and then maybe there's different implementations of particular traits, different trait type parameters, and stuff like this, and yada-yada. And if you look at the documentation for a particular module in Rust, you'll see all these trade implementations listed... And it all feels much more complicated than just the Roc style, where it's like, hey, there's just a flat list of functions in this module. That's it. They're totally ordinary functions. \[00:24:26.25\] And yeah, one of them happens to be named equals, and one of them happens to be named plus, or whatever... But they're not special. They're not magical. They're just there. That is your API. That's what's available in this module. It's all in one place, and it's all just plain functions... And yet, you can still use them in more interesting ways, just by virtue of whether they're sort of designed to work with DOT or not, from the API designers perspective.

Jerod Santo:

That is interesting. So we're down here on this tangent, but the way that we got here was because you were saying how you've allowed user feedback and other people's interests move Roc in certain directions, because you weren't so stuck on being exactly like Elm in every case, but you want to do it a different way... So you were showing how you haven't had strong opinions, I guess, on everything, but you do have some strong opinions as well.

Richard Feldman:

Very true.

Jerod Santo:

There's things like Roc is not self-hosted - that's kind of like a milestone for nerdery. It's like, "When a language self-hosts, now I can finally use it." But you're like "No, we're not going to do that." So that's just an example of one of the things that you're opinionated about. Do you want to talk about that one? Do you want to open up broader about your other opinions?

Richard Feldman:

Yeah. I mean, I'm happy to talk about that, and others. So the self-hosting thing - for those who aren't familiar with that term, self-hosting is where you basically take a programming language that you're working on and you rewrite the compiler in that language.

Jerod Santo:

Right.

Richard Feldman:

So for example Java, originally -- before Java existed, the compiler was necessarily written in something else. Eventually, it got to a level of maturity where they decided to rewrite Java's compiler in Java. Most languages do this. It is super-common to self-host. We have a FAQ entry about why we are intending to never self-host Roc's compiler, and the bottom line is just performance. It's really, really important to me that Roc have an extremely fast compiler. In order to get a compiler that is maximally fast, you need to have certain language features available that are memory unsafe, to be blunt. You just need more control over memory than what is possible if you want to have -- like, Roc is a language with automatic memory management as the only way of doing things. So if we want a maximally fast compiler, either if we want to self-host, we need to add features to Roc that would make it less safe, which I don't want to do. That's like a non-goal for the language. Actually, if anything, we're in the opposite direction, where -- we can go on a tangent about that too, but Roc has a lot of safety features that are unique. Like, I don't know of any language that gives you as strong safety guarantees as Roc can give you... About like trusting third-party code, and stuff like that. We can go on a tangent about that. But basically, if we wanted to self-host and be as fast as possible, we'd have to contort the language in ways that I think would be bad for the language. And if we wanted to self-host and be okay with a slower compiler as a result of that, then we're also working against a different goal, which is we really, really, really want you to have a great experience with a compiler, and a really big component of compiler experience is "How fast is the feedback loop?" We recently -- in the rewrite of the compiler, which is actually in Zig instead of Rust, but these numbers are not because of Zig and Rust... We were seeing something along the lines of -- it's about three to five times as fast as the old compiler. And some benchmarks of like -- we just took some module in the standard library and ported it to the new compiler, and looked at the before and after... And it was something like five or six million lines of code per second that it was able to parse... And I think -- yeah, parse, maybe also parse in format... And that was counting a lot of lines of comments, because this is a standard library thing, so it's pretty heavily documented...

Jerod Santo:

Sure.

Richard Feldman:

And if you take out the comments, it's still parsing two to four million lines of code per second. And that's like a real world, real module. It's not like we just \[unintelligible 00:28:03.25\] ginned up some fake thing. This is just how it was written, we ported it to the new compiler, and those are the numbers we got. \[00:28:11.08\] Now, that's parsing. Parsing is a pretty small percentage of overall compilation time... But I guess it gives you an idea of this is a really, really strong value for us. And I should mention, by the way, that if anyone listening is interested in getting involved in a compiler, we're really contributor friendly. Roc has always been a very, I don't know, community-built project... And because we're doing this big rewrite, there's actually kind of a unique opportunity right now to get involved, if people are interested. Happy to have anyone who wants to get involved in an exciting, high-quality, high-performance compiler.

Jerod Santo:

Awesome. Check the show notes to figure out how to get involved. Everything will be in there. So is it safe to say that -- well, do you describe Roc as a general-purpose programming language? Is that fair, or no?

Richard Feldman:

I think that's fair, but also it's not how I'd describe it, just because I don't describe anything as general purpose languages... I think Evan kind of -- I forget how he said it, but he made a really good point about this, which has kind of stuck with me, which is basically like, "Look, every language is good at some things and worse at others." You can be like "Python is a general-purpose programming language." Oh, cool. So you're going to make an operating system in that? "Well, no. Not like that." Well, but C - you would make an operating system. It's like "Yeah, sure." It's like, oh, okay, so C is a good choice for scripting. "No, no. Not like that." So it's like, what does general purpose really mean? Every language is good at some things and worse at others. But I do think it's fair to say that we want to cast a very wide net. I do want Roc to be good for scripting. That's one of the reasons that you never have to write type annotations. I have an intention of writing a blog post at some point comparing Roc and Python, and maybe TypeScript, and Go, I don't know... TypeScript is probably more reasonable; just to be like "Here, if you write this script in Roc versus Python versus TypeScript, here's how they compare in terms of conciseness", which is important for scripting, "but also in terms of what's the developer experience? What happens if there's an error? How likely is it to go off the rails and blow things up?" All things that you potentially care about. And actually, for scripting in particular, there is a really cool thing that we can do that's uniquely Roc, which is if I get a script from the internet - and this is always the big concern with downloading scripts from the internet. Everyone's like "Never download and pipe to Bash, because you don't know if it's going to blow up your computer." We actually can prevent that. There is a way you can download a Roc script that you got off the internet and changing one line of code in the script from what the author gave you - or maybe the author did this for you... One line of code change and you can be like "I do not care what's in the rest of this script. I know for sure, 100%, no exceptions, when this script runs, it will prompt me every time it's going to do something to my file system, or do something that I might not want the script to do. It has to prompt me, and the script author has no way to work around that." I as the runner of the script, all I have to do is change one -- I don't even need to read the rest of the file, I'll run whatever you gave me and I don't have any fear that it's going to break my computer, because there's this way of basically sandboxing the whole script, even if the author did not want it to be sandboxed, and was trying to do something malicious.

Jerod Santo:

Interesting. So let's say you have a .roc file out there on your server, and I go ahead and download that with my Roc program.

Richard Feldman:

Yes.

Jerod Santo:

Do I then prepend this line into the file, and then execute it? Or how do you actually get that done?

Richard Feldman:

Something like that. We can go on a big technical tangent about this, but... So basically, Roc has this concept of -- we call it platforms and applications. And the basic idea is this. Every Roc program is designed to be sort of embedded in -- you can think of it as kind of like a framework, although a platform is not really a framework, because it's got more responsibilities than that; it's got sort of a bigger scope than a framework. But it kind of comes from this observation of if you're building a web app, let's say in Ruby, you're probably building it on Rails, or Sinatra, or something. You're not really starting from scratch, an empty .rb, no dependencies thing. You're going to build on top of something. \[00:31:54.25\] If you're building a game, you're probably going to build it on a game engine. I know some game programmers are really hardcore and start from scratch, but usually, you're building on top of something that was already there. So we sort of formalize that, and because we formalize that into a first class language concept, there's always a thing you're building on top of. There's a bunch of really cool benefits that we get out of this. So the platform is basically like "Here is a Roc API", so this is the equivalent of your Rails API. Like, "Here's what Rails exposes. Here's the concept of model view and controller that Rails has", and so forth. And then under the hood, there's a lower-level language that's implementing a whole bunch of stuff, including all your IO primitives. So for example, one of the things that's cool about this is that it means that you can get sort of this domain-specific experience, which I loved from Elm, but it's now applied to more different domains. A really simple example of this is if I'm doing a CLI app, I really want that CLI app to have like write to standard out - that's an obvious IO thing that I want to do - and I also want read from standard in. That's also a really common thing to want to do in a CLI.

Jerod Santo:

Sure.

Richard Feldman:

But if you're doing a GUI app, like a desktop app, like a music player or something like that, do you want standard in? Is that a thing? No. Why would you want that as an IO primitive? That's, if anything, kind of a foot gun that if I have some dependency that's blocking on standard in, that's just going to be a bug. On a web server, do you want standard in? Do you care about that? So the point is that there's different IO use cases for different domains. A really clear example of this is in the browser. Like, you mentioned that Roc compiles to web assembly. If you're in the browser, do you want a standard library in your language that has file system IO in it? Rust does. Rust also compiles to web assembly. But the Rust standard library is like "Cool, here's how you open a file and write to a file." And it's like, I can't do that in the browser. So if I have any dependencies that I'm building on that are doing that, I don't actually know what they're going to do in the browser, but it's not going to work, because the browser doesn't have a file system. So what's cool about this is because when you're building your Roc application you have to pick a platform, and the platform not only gives you your domain specific stuff - like, if you're building a web app, you use a web app platform, and it's like "Here's how to do requests and responses." And if you're building a CLI app, it's like "Okay, here's main, and here's how to do your IO for that." But everything is sort of tailored to the exact use case that you're building for. If I'm building a web app, I use a web app platform. And there are already several of those out there in the wild in the Roc ecosystem. If I'm building a CLI app, I do this. If I'm building a graphical app, I do this. Game app, I use this game engine platform... Getting back to the scripting use case - so what you're doing there is you're swapping out the platform. So what you can do is if somebody gives you a Roc app, a .roc file, that's a script that they want you to run, it has to say "Here's my platform." And let's say they used some generic scripting platform... The one line of code that I change is I swap it out for something called SafeScript, which is an API-compatible platform with the one that they used, so everything's still gonna work, except that SafeScript has for its IO implementation under the hood all of these like "Yeah, if you ever tried to read from Etsy Password, I'm gonna stop the script and prompt", and there's no way the script author could do anything about that. We intentionally do not have an arbitrary FFI, foreign function interface where the script author can be like "Haha, I'm gonna sneak in some C code in here and get around yours." It's like, no, no, no; literally, all you get is what the platform provides you. And so if I as the consumer of that just swap out the platform for something that's API-compatible, there's absolutely nothing the script author can do to get that. There's no escape hatch that they can use to circumvent that.

Jerod Santo:

That's pretty cool. So these platforms, are they effectively -- like, if I was gonna implement a platform, it's like an interface, or an API? Or is it another Roc program that I run, or how does that whole deal fit together?

Richard Feldman:

So there's two pieces to every platform. There's the public API, which is what the application authors consume... So one of the hard design constraints of platform authoring is that if -- application authors need to not care what's going on under the hood with the platform. They need to just be like "I don't know. I only know Roc. I don't know any other programming languages. I only know what Roc code is, and so I don't care what you're using behind the scenes." But if you are doing a platform, if you are a platform author, you do need to know not only how to make your public-facing Roc API that application authors are gonna see, but also the lower level piece, which has to be written in some other language. So people have written these -- we call it the host, is like the lower level part... People have written these in Rust, in Zig, and I think maybe there was a C++ one... But you can use any language you want. We also have some -- these are really Hello World level things, but Swift, if you wanna do an iOS app, in the Roc repo you can see these examples. Java - people have done a bunch of different JVM languages where you can just write a .roc file and like "Hey, here it is, running on the JVM." \[00:36:32.17\] One of the cool things about this is it means that Roc is also quite good at getting embedded into existing codebases, because you can basically say like "I'm just gonna write a bespoke one-off platform that's just my whole codebase, and I'm just exposing entry points for the Roc code." And now you can be like "Oh, cool. I have this big Java codebase and I want to write part of it in Roc, because I think the ergonomics are better, or the performance is better..." We also have a really strong value of runtime performance, so we actually compete with -- we want to be faster than Go, but not quite as fast as Rust, or Zig, or C++, because that would require introducing memory unsafety... But faster than all the -- I think Go is the fastest currently garbage collected language. But yeah, so you can just sort of insert yourself into existing codebases, and then that codebase can just kind of import a .roc file. One of the other things that's really cool about Roc is that we don't have a heavyweight runtime. There's no virtual machine, or anything like that. It basically compiles down to the equivalent of - if you're writing Rust code and you're opting into Rust's automatic reference counting all the time, that's very, very close to what you're getting with Roc. Rust programs can run faster than Roc programs, because in Rust you don't have to reference count everything. You can just, in many cases, choose not to do that. But in Roc, that's just sort of done for you automatically, behind the scenes, which makes Roc code a lot more concise and a lot simpler than Rust code, among other things... But it does mean that you don't need to worry about like "Oh, am I going to have a JavaScript VM that's running a Roc VM inside?" It's like, there is no Roc VM. It's just, you just call functions as if they'd been written in C, or Rust, or whatever else, and they just sort of slot in there. **Break**: \[00:38:09.01\]

Jerod Santo:

So how close are you to catching Go, and when and how are you going to catch it?

Richard Feldman:

Well, in terms of popularity or performance?

Jerod Santo:

No, in terms of performance.

Richard Feldman:

So arguably we already have. So this was back in 2021, we did a proof of concept where the benchmark -- I actually gave a talk about this at Strange Loop, it was called "Outperforming imperative with pure functional programming." And basically, we did this benchmark where it was a quick sort on -- I think it was like a million 64-bit floating point numbers, and we compared Roc, Go, Rust... No, it might have been C++. And -- yeah, C++, I think. Roc, Go, C++, Java and JavaScript. And for that task, we just barely edged out Go. We were slightly faster than Go, and C++ was faster than us, obviously. I do think in the new compiler, we will probably be slightly slower than Go at that one, just on the other side of it, because one of the optimizations that we use to get there we decided was not worth it anymore, given our experiences with it... And we're planning on dropping it from the new compiler, so I wouldn't be surprised if that one put us slightly below, instead of slightly ahead on that one particular benchmark. But that was always kind of a silly benchmark in the first place, because it's like, who cares about handwritten textbook quick sort with no handwritten optimizations.

Jerod Santo:

Right.

Richard Feldman:

But it was kind of a good sign. Basically, the short answer for how we can be faster than Go is that, like Go, we do what's called unboxed data structures a lot. So if you have a struct in Go, which would be kind of the equivalent of an object, like a plain, unadorned object in JavaScript, just some data, no runtime information or anything on it - in Go, that's not a heap allocation. It's just plain old stack memory. It's just really straightforward. Same thing in Rust, same thing in C++, same thing in Roc. We also don't heap allocate our closures, which is very unusual for a functional language. We have spent a lot of time making that happen, and one of the reasons for the rewrite was that the approach that we used in the current compiler turned out to have some bugs that we thought were just like "Oh, we just need to squash these bugs and then we'll be fine." It actually turned out the very last sliver of those bugs, which do come up, actually are not fixable incrementally. They require a really serious re-architecting of multiple parts of the compiler. We do have a proof of concept of like if you do that, that does fix it. But the proof of concept is a really tiny subset of Roc, so it's like "Okay, now we need to productionalize that for real." But yeah, so basically, similarly to Rust, we don't box things by default. We do automatic reference counting for memory management, and we use LLVM, much like Rust and C++, for compiler optimizations. And LLVM is just really, really good at optimizing your code. Go does not use LLVM, so the fact that we are sort of at parity in terms of generally speaking how we do memory management, like which things are heap allocated and which ones are not, but we get LLVM on the other side, generally means that we can just kind of be faster than Go. \[00:43:57.24\] The one counterbalancing fact is that we do have more functional APIs. And again, without going on another long tangent - which we can go on I guess a quick one, if you're interested, about our opportunistic mutation...

Jerod Santo:

Sure.

Richard Feldman:

There are API differences that might mean that Go is faster than us at some things, and also vice versa... I guess the brief tangent about that is opportunistic mutation is also something that we are unique in industry focused languages for doing. There's some research languages that do it, so we're not the first to come up with this... But the basic idea is this - in a lot of functional languages you have immutable APIs. You're like "Immutable data is awesome. It has all these really nice properties. It's great." But there's a downside that - if you try to do this in JavaScript, for example, you're going to be defensively cloning stuff all the time, automatically.

Jerod Santo:

Memory.

Richard Feldman:

It's just like, cloning, cloning, cloning, all these things. Oh, it's immutable, but I need to get a new one, so I'm going to clone the whole thing, except one thing is going to be different. That's extremely slow. So what we do is - I call it opportunistic mutation. There's other names for it in compiler literature, like functional, but in place... But that's so long to say.

Jerod Santo:

I like opportunistic manipulation. It's long, it's impressive, and it sounds -- like, it rolls off the tongue. I'm already saying it, so I like that name.

Richard Feldman:

Yeah. So the basic idea is that it's like -- so we do automatic reference counting. Let's say that, for example, I want to append something onto the end of a list. First of all, in most functional languages, list refers to like a linked list. But for us, it's more like JavaScript or Python, where it's actually an array, a flat array in memory. Normally, in JavaScript, you're like "I want to append something onto the end of the list and get back a new list at the end of the day" - that requires cloning the entire existing thing and then sticking something on the end. And now you have the new one. Well, that's terrible for performance, obviously. Much faster is to mutate it in place and just say "Yeah, just stick something on the end. We're done." So the CPU likes that, but as a programmer, if you're trying to do everything with immutable data, because you like the semantics of that and how things sort of chain together, and you don't have to worry about "Wait, did it change at this point, or that point?" It's just like "Oh, everything just kind of flows from one step to another." How do you get the performance and the semantics? And our answer to that is this opportunistic mutation thing. Really, really simple idea. It's basically like, at runtime, we look at the reference count, and we're like okay, if the reference count is one, guess what? Nobody else is observing this thing. So if I just go ahead and stick it on the end, nobody's going to know. It's going to be fine. If the reference count is higher than one, then I can't do that, and I do need to clone it first... But then the clone now has a reference count of one, because I've just made it. So from then on, the rest of the pipeline, it's all going to trigger the opportunistic mutation. So basically, you can think of it as like -- it is potentially doing the "clone your thing", but kind of our hypothesis, and so far this is proven to work out in practice, was that in practice, the way you write your program tends to be you're only passing around things that only are being used in one place most of the time, if you're going to be modifying them anyway. So the amount of cloning that your program needs to do actually should be extremely, extremely minimal. And that's exactly what we've seen in practice. There are sometimes cases where it's like "Well, I wrote my program in this way, but it was doing unintentional cloning, and then I got it to be much faster by kind of refactoring it, to just do things a little bit differently." So far, that's gone fine, as far as educating people when they're like "Hey, my Roc program is slower than I expected. Why?" And then we look at it and like "Oh, just write it in this slightly different way instead of this way, and it'll be totally fine." It kind of remains to be seen what does that look if you have a giant Roc codebase, is it a problem or not. But given the fact that a lot of people write JavaScript in a functional style where it's just cloning all the time... Clearly, it's not a deal breaker for some people. But my hope is that what we've seen so far will continue to scale, where you just get in the habit of writing things in a style that's totally reasonable, and looks nice, and is easy to understand, and just has these really nice properties, and what you get is the performance of a language that's mutating all the time, but the semantics of a language that doesn't even have a formal concept of mutation, except behind the scenes, as a performance optimization.

Jerod Santo:

\[00:47:57.24\] So way back in the day I was writing some Objective-C, because I was trying to be on Apple's platforms. I remember they added automatic reference counting at a certain point, and either I was using it wrong - which is probably the case - or it would get screwed up... And maybe it's because you could do it manually or automatically. But sometimes the count would get off, and things would get funky... And I'm wondering if Arc is -- was that just a circumstance of Apple platform changing over time? Is Arc solid where it's like -- if it says one, it's correct. It's counting correctly no matter what, or could it be off, and \[unintelligible 00:48:32.01\] but there's actually something else referencing it, and the count cut off somehow.

Richard Feldman:

Yeah. In our case, we actually have never seen that be a problem. I guess there might have been -- at some point we had a bug in our standard library, or something. Reference counting - one of the things that I've learned the hard way is that reference counting if done manually is super-error prone. If you're trying to do the increments and decrements -- that was one of the hardest things in the compiler to get right in the first place, was where to insert the increments and decrements. And on top of that, we also have optimizations that are like "Oh, in this case we can see that it's going to increment it", and then later on the same function decrements it before it gets past anywhere... So just eliminate both of those, don't bother. But for example, early on we were trying to do some of this by hand, in some of the early platforms, and I just remember, it's so easy to get it off if you're doing any part of it by hand, even if most of it is automatic... So I'm guessing that that was really a case of Objective-C letting you do it either by hand or automatic, and then the by hand part's messing up the automatic parts.

Jerod Santo:

I totally remember that. I think you're absolutely right. It was like I had some by hand, and then I'm switching over to the automatic, and in that migration there was just no way that I was going to be right, because I had some manual things that would screw up the automatic... And I was like "This is a total mess. I'm better off just managing it myself." But that was in that circumstance. I imagine if it's handling it at the compiler level for you, and you're never doing manual in the programming language itself - it's probably way more reliable.

Richard Feldman:

So if you're just using Roc and you're doing application development, it just feels like a garbage collected language, except that there's no concept of a GC pause, because a traditional market suite garbage collector or tracing garbage collector is the category of those things. There is some moment where they're traversing the heap and finding what can be freed... Modern tracing garbage collectors are much better at this than they used to be in terms of minimizing GC pauses and latency and stuff like that... But one of the reasons that reference counting is pretty cool on applications like UI development is just that it's totally incremental. It's like, you're just constantly freeing little bits here and there as the program is running... So you don't need to do something super-fancy to avoid pauses, because you just naturally don't have pauses. It's all spread out over all the different allocations in the program.

Jerod Santo:

That's really cool. I mean, take that, Go. Come on.

Richard Feldman:

\[laughs\] Yeah.

Jerod Santo:

\[unintelligible 00:50:53.26\]

Richard Feldman:

I mean, in all seriousness, part of the reason that we like to be competitive with Go is just that I think Go does a really good job of delivering really good runtime performance and really good compile time performance. But I would rather use a language with Roc semantics than a language with Go semantics... But I still want those things. I still want it -- we want to compile faster than Go and run faster than Go, because... Why settle? We have the potential to design things in a way where we can pull that off, and we want to pull it off. Realizing those dreams is -- I don't know, it's been exciting every time we've gotten a result where we're like "Nice, it actually wasn't just possible in theory, we pulled it off in practice." And I'm really excited to ship 0.1.0 and let people actually try out the whole experience.

Jerod Santo:

Yeah, that's going to be rad. Well, you mentioned Go, and -- or maybe I did. We both did. And it made me think of if err not equal nil, because that's what I think of with Go... Which makes me think about error handling, it makes me think about undefineds and nulls... Because oftentimes that's what you're checking all the time. And Roc does not have a null, or a nil, or an undefined... It also does not have an optional maybe thing... But still, sometimes you have a thing, sometimes you don't, so... How do you handle the circumstance where you might be getting back something and you might not?

Richard Feldman:

\[00:52:16.09\] Yeah. So I'm biased, but I think Roc has the best story of this of any programming language. But I'm biased. \[laughs\]

Jerod Santo:

Let's hear it. Convince me.

Richard Feldman:

So let me explain how it works. So although we don't have something like option or maybe, we do have result. And result works pretty much the way that it works in Rust, or OCaml, or something that, which is pretty much it's like maybe, except that you have the success, but also you have an error type. So maybe is like "I either have this thing or I don't", so much like null. Result is like "I either have this thing or I have something else", that's like an error that explains what went wrong. Oftentimes that could just be a string that's like "Oh, I couldn't find this thing because of whatever reason." And a special case of that is you can just have nothing for the error type, which is like an empty error type, where there's no extra info... And that kind of works the same way as maybe. The reason that we have that particular design is centralizing everything around result means that you can improve a lot of ergonomics and say "This is just for saying "I either have this thing or I don't" when you're returning it from something, as opposed to using it for data modeling as well. So it would be really weird to have -- for example, in your data model you're representing "Here's a user" or something, and maybe they do or do not have an email address... It would be weird to put a result in there and say "Oh, they either have an email address, or error no email address in my data model." That's more of like a functions return result, because it's like "This operation succeeded or it failed." It's not really about data. That's the FAQ entry of like "Why did we choose not to have option?" So we do still have result. Now, the reason I think we're the best at error handling is actually something that's more to do with what we call anonymous some types. So a lot of programming languages have a concept of -- I guess the most common term I've heard for this is algebraic data type. So this is something that a lot of people have said they want in Go... And the low level version of this is what's called a tagged union. That's what Zig calls them, for example, or C. And basically what it is, it's just like you have some series of alternatives... So you can say -- let's use traffic light colors. You have red, green and blue. A lot of languages have that concept, and it's called an enum. So it's an enumeration of three different options; red, green and blue. It's exhaustive, meaning that if you have one of these stoplight color values, you only have red or green or blue. There's no such thing as like "other". That's not really a thing.

Jerod Santo:

Right.

Richard Feldman:

Now, if you want to introduce a concept of other, what algebraic data types let you do is you can say "Red --" I said blue. What color are traffic lights?

Jerod Santo:

Red, green and blue. I don't know. I was just rolling with it.

Richard Feldman:

I'm often in like graphics land, right? RGB... So in this hypothetical world we have traffic lights that have blue in them. No, let's go red, green and yellow, like actual traffic lights, just different order.

Jerod Santo:

Okay, fair.

Richard Feldman:

So let's say that you want to expand that and you do want to introduce a concept of other. What you can do is you can say "red, green, yellow, other", and then other has what we call a payload, which means it has some additional data associated with it. So you could have other and then a string payload that basically says "Okay, it's either red, it's green, or it's yellow, or if it's other, then I have this string that describes what the other color was." And the critical distinction there is that if I have red, green or yellow, I don't have that payload. Those are just like "No, it's just the value of nothing in there." Only if I'm in the other case do I have this extra piece of data. Now, in a lot of cases, what you end up doing with algebraic data types like this is you end up having payloads in all of the different options, or most of them, or all of them but one, or something like that. And so what's really nice about them is it allows you to really specifically say "Okay, under this circumstance I need to provide this extra data, and under this circumstance, I provide this other, totally different shape of data." And it just basically unlocks this really nice experience of modeling your data in a way where you can say "Okay, under this scenario, I have this to work with. Under this scenario, I have this to work with." \[00:56:17.18\] And also, when you're constructing that data, it's like, "Okay, if I'm in this scenario, I have to provide these pieces of information or else I cannot get one of these values." And so it lines up really nicely between "Here's what I need to provide if I'm in this situation", if I'm saying "Hey, we're in this situation", and also "Here's what I have access to when I'm in this situation." And then the exhaustiveness piece of that is still there, where when you're extracting a value out of this, it's like -- this is where pattern matching comes up. You can say "Okay, if I'm in the red scenario, do this logic." Like a switch statement, or something like that.

Jerod Santo:

Sure.

Richard Feldman:

"If I'm in green, do this. If I'm in yellow, do this. And if I'm in other, do this, but also, only if I'm in other do I have access to that string. And in none of the other branches do I have access to that." So you can do this in TypeScript, but it's pretty clunky; in languages like Roc and Rust and stuff where this is a first class thing, it's super-ergonomic to do that. Okay, that's algebraic data types... But what Roc has is the anonymous version of that. So everything I just told you is true, and you can do all that stuff in Roc, but we have one extra thing, which is that if you want, you can also build these up on the fly. So for example, instead of defining up front "We have traffic light colors which are red, green, yellow and other", I can just write a totally normal conditional that's like "Olay, if this is true, blah, blah, blah, set this variable equal to red. And in another branch, set this variable equal to green. And in another branch, set this variable equal to yellow." And Roc's compiler will just infer based on my having used that anonymously, pretty much exactly the same way that it'll infer things based on like if you just use curly braces to make an anonymous - we call them records, but anonymous object in JavaScript, or something like that. You don't define anything up front. You just use them, and it just says "Oh, cool. I will infer that the type of that variable that you've been setting to red, green or yellow or other with a string in it is just like, yeah, it's either it's red or it's green or it's yellow or it's other with a string in it. No problem. I'll just infer that, and you don't need to define anything up front." Now, what's really cool about this is how it applies to error handling. Because now what you can do is you can say "Okay--" Let's say I'm trying to read from a file. This is a classic example I like to give of why this error handling is really nice. I'm reading from a file, that file contains a URL; inside that URL -- I take that URL, I do an HTTP request to that URL, I get back the response, and the response says "Here's some information I want to write to a different file." So this is a file read, HTTP request, file write. All of those can fail in different ways. What's really nice about Roc is that when you just call those, it just looks like the most straightforward, just TypeScript, Go whatever, you just call the functions and just do the IO and it just happens. But behind the scenes, the compiler is just tracking automatically, using this feature, "Here's all the errors that could happen", and it's just unioning them together. And at the end of the day, what that function will return that does those three calls is just like "Okay, it's a result which either has "I succeeded", in which case here's the answer of all those things..." If any one of them failed, you have an error type that is just an algebraic data type of the union of all the things that could go wrong. So it could be like network failure from the HTTP request. It could be like file system was read only from the file write. Or it could be file not found from the read. All those things just get put together and you can just do a pattern match on them and it's just like "Here are all the possibilities." The compiler will tell you if you forgot one, because it's like "Hey, there was this error that you didn't handle, that was in the union of these things." But at no point did you have to specify anything. You can write no type annotations anywhere in that program. You just call the three functions just totally normally, and then you're like -- yeah, it just accumulates the errors that can happen. \[00:59:59.11\] I guess one detail I did leave off is that we do do something that Rust does, where the sort of error propagation is explicit. Rust does this with a question mark operator at the end. We do the same thing. So basically, if you want to say "Run this IO operation, and if it failed, do an early return with the error." You put a question mark at the end of that function call, after the close paren. And this is nice, because it means that all of your control flow is explicit, unlike exceptions, where you have to defensively try catch. It's kind of inverting that, where it's like "Hey, if this might cause an early return with an error, you can see where those happen, because there are question marks there."

Jerod Santo:

You also use bangs in a certain way that I couldn't tell if it was idiom or enforced. I'm assuming it's enforced. The exclamation mark at the end of the call means something. I don't remember what it means. And is it real? Or is it like Ruby, where it's like "It should mean that, but you could use it whenever you want..."

Richard Feldman:

You know, it's funny... It's Ruby, but it is enforced with a compiler warning. So this is what we call purity inference. So essentially, the way that effects work in Roc - this is also different from... I don't know of any other functional language - or imperative language for that matter - that does this. But the basic idea is super-simple. Because Roc's APIs are designed to be based around immutable data, you can very, very easily write a function that is doing quite a lot of stuff, and yet it's a pure function. Pure function, for those who don't know - short definition is a pure function is one where if you call it, passing the same arguments, you are guaranteed, 100% guaranteed to get the same result back, the same answer every single time; same arguments, same result. And also, it doesn't do any side effects. It doesn't affect other parts of the program in an observable way. So we actually have a concept in the type system of which functions are pure and which functions are effectful. So all the IO functions I just mentioned, those are effectful. The way you can tell the difference - there's two ways. From a type's perspective, it's a really, really subtle distinction, but basically, if you have the list of arguments for the function, and then you have an arrow and then the return type - that's the type signature for a function. If it's a thin arrow, like dash greater than, that's a pure function. If it's a thick arrow, that is equals greater than, that's an effectful function. And so we have this convention, which again, is enforced by the compiler, that effectful functions have an exclamation point at the end of their name. Actually, we did take that from Ruby. Ruby has that for destructive updates, and stuff like that. So basically, for example reading from a file, it'll say \[unintelligible 01:02:24.12\] And that tells you that when you call this function, it's going to be effectful. Now, the compiler also enforces that only effectful functions can call other effectful functions. Pure functions are not allowed to call effectful functions. And that's not because we're trying to be mean, that's just the definition of a pure function. That's one of the side effects.

Jerod Santo:

Right. Otherwise you're not pure, dude.

Richard Feldman:

Right. And one of the ways that we actually make use of this is that -- this isn't just like, you know, we're trying to organize things for the sake of organizing them. One of the things that the new compiler is doing is that if you're writing a top-level constant, like you're saying \[unintelligible 01:02:58.00\] just at the top of the file, not inside a function or anything like that, we actually will -- so first of all, you're only allowed to call pure functions in those constants. You can't be like "foo equals" at the top of your file, outside of any function, and just run effects in it. But because you're only calling pure functions, we actually will evaluate that entire thing at compile time. And you can make that as complicated as you want. So you can do really complicated transformations, and not have to hard code so many things. You can call whatever functions you want, as long as they're all pure, and the compiler can evaluate them all at compile time, because it knows "Yeah, they're pure functions. They don't have any side effects. It's fine." You just do that. One of the things that's cool about this, going back to - earlier on we were talking about one of Roc's goals is to be really good at getting embedded in other things... Not only do we not have a virtual machine, but also, we don't even have a concept of init. You don't have to boot up a Roc program. You can just call the entry points, and whatever happens happens... Because all of our constants get compiled all the way down to just plain flat values in the compiled Roc binary, which means that there's no initialization. You can write, whatever, "foo equals", and then have a bunch of initialization logic, but that initialization happens at compile time rather than at runtime, just using your ordinary plain Roc code, as long as they're all pure functions. \[01:04:18.15\] So that's a concrete example of a way that the Roc compiler itself is using purity for benefits, but also pure functions just have all these really nice properties, such as if you're calling a pure function and you're like "Oh, I want to cache this thing because it's kind of expensive, it's like, no problem." You just use the arguments as the cache key, and it's definitely going to work out.

Jerod Santo:

Right.

Richard Feldman:

So there's a bunch of stuff that. And with concurrency, you can run pure functions concurrently, and there's no problem... A lot of stuff like that.

Jerod Santo:

That's awesome. So in a world where I'm doing operator overloading, as you described earlier, and I'm overloading the plus pure function, I can't go shove in some effectual stuff in there. I can't call file.write inside of there, could I?

Richard Feldman:

Great question. I guess the short answer is it depends. So first of all, because of the naming convention, if you tried to name it plus without the exclamation point, and it was effectful, you would get a compiler warning, because it's like "Hey, you're supposed to put an exclamation point there." And then if you add the exclamation point, then the sugar is not going to work anymore.

Jerod Santo:

Right. So compiler warning is the level of enforcement. Like, I could still get away with it, but the compiler is going to tell me "Hey, this is a bad idea."

Richard Feldman:

Ah, okay, so that is - yes, you're right. But also, we do take it a step further, which is that -- so one of the things that's certainly true is that oftentimes, even if it's not something I want to ship to production, I do want to use IO in the middle of some pure function just for debugging purposes. For example, I'm really lost in the middle of this thing... I just want to write to a file what's happening right now, so I can debug it. We also make it so that that is a compiler warning. I should give the caveat that that doesn't work for the constants use case, when you're doing it in the middle of a constant. But that's happening at compile time, so then we actually just don't know what IO is, because that's a platform thing, so that's not available... So that is a hard error. But in general though, this is actually part of Roc's design philosophy - I've been calling it "Inform, but don't block." And what I mean by that is basically we try to make everything to the extent possible non-blocking in terms of compilation problems. So we will give you a warning... And also, we are kind of hardcore about warnings, in that if there are any warnings when you build, the compiler exits with a non-zero error code. So that'll fail your CI if there's any warning... So warnings are taken seriously. It's not like "Ah, it's warnings. Don't worry about it." It's like "No, you need to fix all your warnings." Having said that, it's also true that, for example, if you have a compile error at build time, one of my pet peeves about compiled languages in general is that they will pretty much all - with one exception; Haskell has a flag where you can kind of turn off part of this - it's like if I have anything wrong with any part of my entire codebase, I can't run any of my tests until I fix every single one of those. That always really bugged me, because when I spent a lot of my career in working in dynamic languages, it's like, you could always run any test you want. And that really unlocked all these really nice workflows where I could be like "Oh, I'm going to try out this thing and experiment with it, and if I like how it's going, then I'll commit to continuing further with it." But oftentimes I'd try it out, and even though I know there was a bunch of broken stuff around it, just by being able to try it out or write some tests around it led me to realize "Oh, you know what? I actually want to go in a different direction, and I'm really glad that I didn't overcommit to this and have to fix absolutely everything." And I really want to preserve that in Roc. So what we do is basically we, as much as possible, will say "Hey, I'm going to tell you about this error. If you actually run this code and we get to this code path at runtime, it's going to crash, because we can't--" In some cases it's like "There's nothing we can do about it", like a naming error. If you reference a variable that doesn't exist, it's like "Look, I'll tell you about it at compile time... And if you run the code, as long as you never hit that variable, we can run the rest of your program, no problem. But if you get there, we're going to have to crash, because we can't proceed. We don't know what variable you're talking about."

Jerod Santo:

Yeah.

Richard Feldman:

\[01:08:09.16\] But the idea is inform, but don't block. Always tell you about all the problems we know about at compile time. Give warnings, give errors... But don't block you. Let you run the program anyway. Let you run your tests anyway. Because oftentimes, that's just the better workflow. So this is another area where -- I roll that into developer experience, of like just giving you the flexibility of working in different ways, depending on what you're trying to do.

Jerod Santo:

On the topic of going to production, what does the deployment, or the shipping, the sharing, the distribution story look like?

Richard Feldman:

Yeah. By default, very similar to Go, in that it just spits out a compiled binary that you can just run on a machine. We also do cross compilation. So this is something that a number of languages have... So basically, you can just say like \[unintelligible 01:08:50.21\] X64 Linux. And even if you're on a Mac, it'll spit out a binary that you can just go hand off to X64 Linux.

Jerod Santo:

Nice.

Richard Feldman:

In practice, I guess that means that you don't need to have a Windows CI and a Mac CI and a Linux CI. You can just have one CI of whatever you want, and it can build for Mac and Windows and Linux and all those things. So nobody needs to have the Roc compiler on their machine to run something that Roc built. Separately, you can also build to a dynamically-linked library. So this would be like if you want to use Roc for like plugins, like editor extensions or something like that... Anything that can load a C-based plugin or something like that... That includes programming languages. I had a -- actually, you can see this in the repo. You can try this out. Ruby actually will let you import compiled C modules, just straight up. So if you go to GitHub, we have this in the Examples directory. You can basically compile Roc to a dynamically linked library that just says Hello World, or whatever. Load up IRB and just import that module, and it's like "Hello from Roc." Just straight up. No fanciness needed. And then of course, the third way is web assembly. Like, you can compile Roc directly to compiled web assembly binaries.

Jerod Santo:

It sounds almost too good to be true. What are the downsides, Richard? What are the downsides?

Richard Feldman:

Oh, sure. I mean, well, an obvious major downside today is that the current compiler, although it works -- so first of all, the static dispatch stuff doesn't exist in the current compiler. That's one of the reasons for the rewrite, is that we hadn't figured that design out yet. So what you get today is not as cool as some of the stuff we talked about earlier. Second, there are also, like I mentioned with the -- it's called Lambda set specialization... The thing with the unboxed closures; there are known bugs with that that are blocking certain -- people tried to implement certain really cool projects in Roc nd they got stuck, because although if you're doing simple stuff, applications, they don't come up, but as soon as you start trying to do really fancy platforms, they'll unlock a bunch of other things. They got stuck on these Lambda set bugs that were like "Oh, in order to fix that, we've got to do a big rewrite."

Jerod Santo:

A big rewrite, yeah.

Richard Feldman:

So those limitations - definitely big downsides. But let's pretend that we've solved all those, we have the new compiler, it's 2026...

Jerod Santo:

Advent of code... Exactly. We're here. We've arrived.

Richard Feldman:

Yeah. So now I would say -- so these are downsides that are sort of like downsides we accept as like long term downsides. Number one - I'll just get this out of the way. First, it's different. It's like, you can't be much better if you're not going to be much different, so there's going to be a learning curve. It looks pretty familiar, I think; a lot of people who are used to mainstream programming languages will look at the code and be like "Cool, I can follow what's going on." But there are going to be some semantic differences. For example, since we don't have a first class concept of mutation, there is no array.push. It's like "Oh, it's always going to be based on append." And there are implications to that, where code gets structured a little bit differently, I would say in a nicer way... But there is kind of a ramp-up there. Another thing is that, again, because we don't have a first class concept of mutation -- and this is very much because the language design philosophy is simple language, small set of primitives, simple type system... Simple, simple, simple. Be simple but powerful, and very ergonomic. There are some algorithms which -- actually, the reason I picked quicksort is because if you try to write that in a purely functional style... We didn't have for loops back then. The new compiler is going to have for loops, but in a very limited way, where they don't affect purity, and stuff like that; or can be used without purity. That's actually a controversial thing in functional programming circles, but if you're not into functional programming, it's like "Yeah, for loops. Sure, sometimes I want a for loop."

Jerod Santo:

\[01:12:22.27\] Yeah. Sometimes they're nice.

Richard Feldman:

But imagine Haskell having a for loop. It's inconceivable.

Jerod Santo:

Well, I do work in Elixir, so I do know functional \[unintelligible 01:12:29.26\] for loops right there.

Richard Feldman:

Oh, that's funny... So by coincidence, as we're recording this, the most recent episode of -- so I have a podcast called Software Unscripted. We do a lot of technical deep dives, and stuff. I just had on Jose Valim as the most recent guest...

Jerod Santo:

Nice.

Richard Feldman:

...and literally, we were talking about for loops, and he was talking about the challenges of trying to get for loops into Elixir, which I guess he's been trying to do, but they never have been able to get a design that quite checks all the boxes...

Jerod Santo:

Right.

Richard Feldman:

It was a fun conversation. But in some languages - yeah, what did he say? It was like "Some people have accused me of being a sleeper agent, because I'm trying to get for loops into Elixir." \[laughter\] And without going on another tangent about language design and for loops, that is an example of something where I feel confident enough in my love for functional programming that I'm like "Yeah, no, this is a good fit for this language to have for loops, because sometimes that's the best way to write the thing." It's just like, an imperative for loop is just the most straightforward, nicest code to read for that particular use case. I would also bring that up as an example of a downside, like you were saying earlier, because there are just some things where it's like the nicest way to write it is to actually have a first class concept of mutation. And the fact that we are, as a simplification, not including that in the language means that you might have to write it in a way that is not quite as nice as if you had that tool in your toolbox. Now, of course, having that tool in your toolbox opens a huge can of worms in terms of like now your functions don't have the same guarantees, you might have to do more defensive cloning, yada, yada... But it is a tradeoff that I'm willing to acknowledge... In the spirit of that FAQ, of like "No, we think this is worth it. It's the right way to go." You can't have simplicity without subtracting some things, so there are some things that we've intentionally subtracted, and that means you have a simpler language, but it means that some things I acknowledge are going to be less ergonomic than if we had more tools for them. This kind of reminded me - the topic of for loops and whatnot - of another thing that, I guess, makes Roc unusual compared to other functional languages, which is that we do have that concept of first class effects where you have the exclamation point and not... And an implication of that is that you do end up with a form of like "What color is your function?" So if you have a deeply nested call of pure functions and you're like "Oh, I actually want to do file IO in the leaf node here that's got this huge stack of calls above it..." Well, guess what? You have to convert all those to effectual functions now. I would argue that unlike the "What color is your function?", this is not -- it's just the fact about pure functions. Yeah, if you do that, it's not a pure function anymore, and this is just Roc's type system telling you about that. But again, if you're in an imperative language that doesn't track those things and doesn't have that in there, you don't have that downside. You can just introduce IO at any point and not have to convert anything. Of course, then that comes with the trade off of like "Do you know whether your functions are pure or not?" So those would come to mind as concrete examples. I would also say the fact that -- I mentioned earlier about the scripting use case... We intentionally do not have an arbitrary C FFI. Only the platform gets to do low level things. The application just doesn't get to do any of them, period. If you want, you can design a platform that says "Hey, here's a way where you I expose a function that's like "Give me a string", and I'll load a dynamic library off of that", and you can do stuff with that. But it has to be done through the platform, and that does kind of -- you know, FFIs are have downsides in terms of security and other things, but they also have upsides. And so the fact that Roc doesn't have one of those - that's another downside. So I could go on for a while about downsides of Roc, but...

Jerod Santo:

Yeah, that would be against your best interests, so I'll stop you right there. What's the library story? How do I work with other people's code?

Richard Feldman:

\[01:16:08.23\] Yeah, so right now it's very simple. We have plans to make it a little bit fancier, but while still preserving the properties that I want to preserve... So the nicest thing about it, I would say, is that right now if you -- so I mentioned, with Roc we want to be good at scripting. One of the cool things about how Roc can be nice for scripting is that if you want to have a single .roc file and that's all you're distributing to people, you don't have to give them anything else, just one .roc file and they can run it, that .roc file can have dependencies in it, because there's no separate pkg.json file like that. It's just like, you literally put them in your Roc code at the top and you say "I want this dependency, and I want this dependency." They can all just be in one file if you want. The dependencies are based on a URL, and one of the cool things about this is that I don't know any other language that's doing this, actually... The URLs have to be in a specific format. Right now, the specific format is that basically at the end of the URL has to be a hash of the contents of what's behind that URL. So this is a security feature. So the basic idea here is if I have this URL and it's downloading this package, and I'm putting it on my system - what happens if that URL gets compromised, and now somebody puts a malicious thing there? I don't want to download that automatically anymore. That's really bad for me. So the fact that we have the hash baked in means if somebody does compromise that URL, the worst thing they can do is take it down, and make it 404, or something. They can't actually give me something malicious, because if I do, it's going to fail the check that Roc does after it downloads the things. It'll be like "Oh, this doesn't match the hash that was in the URL." So that's a really nice security feature. And of course, if they want to change the hash - well, now they have to change the URL, so the URL is going to 404. A really basic, really simple security measure. Once it's downloaded, it gets cached on your machine. Also, we don't have the equivalent of like a Node modules directory in your local folder. It's all just in a global, immutable cache in your home directory, which means that, again, thinking about scripting as a use case, you can say "Here are my dependencies." There's also no Npm install step. It's just like, the compiler -- you just do roc run and the name of a .roc file. It just automatically downloads your dependencies into your home directory if they're not already there. If they are there, great. We don't need to redownload them again. It also doesn't need to contact the central package index for updates. It's just like "Yeah, they're either in the directory, or there's the URL." And then basically, when it runs, it also doesn't need to do any caching in your local home directory. The design we have for caching is also going to be in the home directory rather than the local directory... So much like with zero dependency Python, for example, if you run a script, it's like, it's not going to put any garbage in the directory it runs. It's just going to do everything in a cache directory, in the home directory, and then just run, and that's it. We also have a design for version ranges, which is actually a little bit based on how Go does those, with their minimum version selection. The basic version of that is in addition to the URL having the hash, we're also going to have a concept of like you can put a version number in the URL, and then the compiler will automatically select a version from those based on the what it finds across all the different URLs you ask for. And they can ask for versions of one another, yada, yada... And yeah, there's a whole design for that, but we have not implemented that yet. But the new compiler will have it.

Jerod Santo:

Yeah, versioning was what I was going to ask you next. So you hit that one off. Once you set a hash in the URL, I'm thinking, "How do you actually deal with what version you want?"

Richard Feldman:

Yeah. The really simple answer there is that basically the thing that we're taking from Go's -- and they have this whole long blog post about the design of minimum version selection. It's a really interesting read. As I understand it, they've kind of changed it a little bit from what they wrote there, but... The basic idea is this - each of your dependencies needs to say "Okay, I depend on at least this version of this file." And we treat different major versions as sort of basically different packages. They might as well have a different name, because they're just like -- yeah, if you have a different major version, they're not possibly compatible, or they're potentially incompatible, so we don't select them. But if you have different minor versions, that's fine. What we will select is just "What is the lowest minor version that we can get away with, while still satisfying everybody's constraints?" \[01:20:01.18\] So if package A -- like, I depend on package A, and it says "Oh, I need--" I'm going to use Bugsnag as an example. That was the error handling thing we used at my last web dev job. And you can say, "Okay, great. Package A depends on Bugsnag version 1.2.3, and package B that I depend on depends on Bugsnag version 3.4.5." No. Actually I need them to be the same major version. So 1.2.4, let's say. 1.2.3 to 1.2.4. So each of those URLs has the hash in it. I know exactly what 1.2.3 is and what 1.2.4 is, and I have the hashes for both, etc. It's all just the same URL-based design that I described before. What the compiler can then do is it can say, "Oh, well, I see that you have, among all your dependencies, in the 1.x.y range", where the major version number is one, "the highest one that we need is 1.2.4. Great. We'll select that and use that for both of these two things." The one that needs 1.2.3 also gets 1.2.4. And that should be fine, because they should be API-compatible. We also want to do the thing that Elm does, which is where you basically have the compiler awareness of what major versions mean, and you can just tell people when they're publishing "Hey, this needs to be a major version bump, because you actually made a breaking change to your API compared to the old one." This is something Elm hardcore enforces. I'm not sure if we're going to be as hardcore about the enforcement, just because of the URL-based thing. It's a little bit more complicated if you're a little bit more distributed and less centralized in how you're getting dependencies... But it is really cool that in Elm if you try to publish a major breaking change where it's like, "Yeah, this actually is API incompatible with the previous version I published", Elm is like "Nope, you need to bump the major version number. That's not optional here, because I can see that you made a breaking change. I looked at the diffs and the types." So we want to do the same thing, at the very least inform you. So we don't ever want it to be the case that someone publishes a new version and is surprised that it doesn't build. And the compiler is going to rely on that. So it's like, when it's selecting these versions, it's assuming that 1.2.3 and 1.2.4 are at least API compatible, and your code will still build, even though obviously they might not be bug-compatible. Maybe you were relying on a bug in 1.2.3, or something... But that is kind of the price we pay for code sharing.

Jerod Santo:

Yeah, a hundred percent. How good is Claude at writing Roc code?

Richard Feldman:

So we actually have something at roc-lang.org in the docs section on the built-ins, we have a little -- this is for LLMs, basically, and it's a little markdown document that you can just either copy-paste in the LLM, or just put in your .rules file, or whatever... It's basically like "Hey, here's what this language is all about."

Jerod Santo:

Really?

Richard Feldman:

Yeah, it's a little primer for a large language model.

Jerod Santo:

That's cool. Does that work pretty well? 3: Yeah, I mean, it's hard to say because there aren't really any big Roc codebases so far, but definitely, if you give it enough examples like that... I've actually -- even without using that thing, I've had good experiences with it. I've opened a .roc file and I'm like "Hey, look at this thing. Can you write some new function in that style?" The funniest thing that I've seen it do though is sometimes, because Roc is really not super-representative \[unintelligible 01:23:07.23\] when there's something that it has not seen any examples of, it'll just make something up. And quite often, it will guess something from Elm, or Haskell, or some other functional language... Because it's just like, "This feels kind of like a functional thing. So maybe it's this", and it'll just kind of throw it in there. And usually, when that happens, I kind of chuckle and I'm like "No, no, it's actually this in Roc." So it's obviously not going to be as good as languages that are really heavily in this training set... I actually view that as kind of similar to the ecosystem thing, where historically, whenever you make a new programming language, people would always say "Well, no one will ever use this, because it doesn't have the ecosystem of gigantically popular language A." It's like, yeah, okay, but new languages do come up and exist, and they weren't there before... Like Rust, for example; people would have said "Oh, nobody use Rust, because it doesn't have the C++ ecosystem." It's like, yeah, but then that happens over time. And it's the same thing with large language models. It's like, no one will use this because it's not in the training set. Okay, I know, that's a downside at first. When you're an early adopter, you have to deal with it. Occasionally it hallucinates some Haskell in there.

Jerod Santo:

\[laughs\]

Richard Feldman:

\[01:24:13.08\] But in the same way that the ecosystem is small at first, but over time it grows... And then that stops being a downside once you get a certain amount of adoption. And the tradeoff is as an early adopter, you get to be a lot more of a voice, a more prominent contributor to the community, because you got in early, when it was not the most polished.

Jerod Santo:

Right. I was speaking with Mads Torgersen recently... He's the lead designer on C\#. And we were kind of lamenting that it's probably never been harder to break out, though, as a new programming language, than it is now, because of these tools and the selection bias, kind of like the rich stay richer effect of an LLM either choosing a tool or a language because you don't care if you're vibing it, or just not being as ergonomical or as useful to you... And so maybe you just pick Python because it knows it better, whereas you're kind of interested in Roc, but you're like "I'm not going to get much help here." I feel like it's going to be harder and harder to actually break out in the next few years. What do you think?

Richard Feldman:

So I thought that... My opinion on that has actually completely 180-ed since I actually tried it.

Jerod Santo:

Well, let's hear it.

Richard Feldman:

So the experience that I had was -- like, intuitively, that makes sense. So the experience I had is, as I mentioned earlier, we did recently - well, recently is like several months ago - decide to rewrite the compiler in Zig... I had done some Zig, but I'd really not used it super in \[unintelligible 01:25:33.16\] for a big codebase before, so there was a lot of learning that I had to do. What I've found was that a) Zig, even though it's a pretty niche language today, is plenty well represented in Claude's dataset that you can just kind of jam on it. It doesn't really hallucinate. I've not seen any problems with Claude 4 having problems with that. But what's really great about it is that I know from plenty of years of experience of trying out new languages that there is always a ramp up period when you're trying a new language. That's always been a really significant downside, which is like, I don't know what character to type next. I know what I want it to do, but I don't know how to say that in this language. And when I hit that roadblock, I always have to go find documentation. But maybe it's like a weird symbol, and so I can't google for it effectively, and I'm like "Oh, what part of the tutorial is going to talk about this thing?" Or like "I know conceptually what I want to do, but I don't exactly know even what to search for." I'm just like "I have this thing that I want to do, and I know the language could do it, I just don't know how to do it." That problem is gone in the large language model world, because I just tell the large language model "Hey, I want to do this thing. I don't know how to do it in Zig", and it's like "Here you go. I just wrote it for you in Zig." And I look at it and I'm like "I can guess what this code does. It looks it does what I want. And if I want, now I know exactly what this code is." I can go look up the docs if I'm concerned about that. It feels so much less choppy and stumbling around in the darky to get ramped up on a language compared to the old days, where we didn't have access to these tools. The new user experience, if it's sufficiently represented in the dataset - but even if it's not, like I said, you can help the model out with this thing - it feels so much easier to get into it. And the other big downside that I mentioned, the historical thing that everyone always talked about, was the ecosystem. You know what tool is really, really good at taking an existing library and porting it to a new language and taking out 95% of the time-consuming drudgery of that, so you could just kind of review it? Large language models. If you want to get your favorite hashing function in Roc, for example, that's something that porting that from, whatever, language A, to to Roc by hand - really, really painstaking and error prone and annoying. Now I can just be like "Hey, okay, Claude, port over all the tests first. I want to make sure all the tests are in place, and I'll hand review the tests to make sure that, okay, these are doing the same things as the other tests in that repo. Great. Alright. Now start porting over the implementation. Oh, tests failed? Great. Go fix them. You have all the source code on both sides." \[01:28:01.21\] The amount of time that it should take to bootstrap an ecosystem and get it to a point where -- it's not going to be as big as big, longstanding ecosystems, but getting it to a point where the ecosystem is not really a blocker for people, and they can kind of get into the language and reach for hashing, or whatever, Bugsnag off the shelf, I think should be a lot faster. So putting those things together, I'm like, "I actually think it might be easier than ever for a new language to break out", especially because if you're like "I'm in this big codebase that I have, and I want to start using a new language...", it's easier than ever to start writing code in that language with the help of a large language model. And if I'm just going to be mostly writing in English anyway to synthesize the new code, why don't I have it synthesize code that's easier to read and to review, and that has nicer properties to be like "Oh, this is a pure function, so I don't have to go worry about what the implications are." There's a lot of advantages to reviewing code that you get from \[unintelligible 01:29:00.06\] languages. But if you're just telling the model what code to write in the first place, why would I tell it to generate Python when I could tell it to generate Roc?

Jerod Santo:

Gotcha. That's interesting. Yeah, I can definitely see that. If I was going to pick up Roc tomorrow and I didn't have an actual use case or... Care in the world. I just want to learn and write some Roc code. I just want the best Roc experience. What would I build where Roc really shines, and I could be like "Oh, okay, I get this." What kind of a thing would I build?

Richard Feldman:

That's a great question. I mean, I don't usually think about it in those terms, so I would usually turn it around and say "What do you want to build?" I would say -- I mean, the three things that I think Roc is currently the best at would be command line apps, server side web apps, and... There is a platform for doing native graphical applications. If you want something that's a little bit more fun, there's this thing called Wasm 4, which is really lo-fi retro gaming kind of stuff in the browser... So people have made a maze thing, and Rocky Bird, which is like Flappy Bird... So if you want to do some game stuff, I would recommend that one. We don't really have a -- there is no serious game dev Roc platform yet. Nobody's built it. But if someone wants to, hit me up. Also, there have been some doing Roc in the browser for web dev frontend stuff. You certainly can do that, but there isn't anything really mature, so I wouldn't recommend that. I would say servers and CLIs are the two that are really the most robust and well used. So I would pick one of those two, if that's something that you want to try Roc out with.

Jerod Santo:

Cool. And where does the Roc community hang out? Where are they?

Richard Feldman:

Zulip.

Jerod Santo:

Zulip!

Richard Feldman:

Yeah. So on the website roc-lang.org -- yeah, we have a Zulip instance. It's definitely the place to go if you want to chat about Roc things.

Jerod Santo:

Very cool. We are Zulip users ourselves, and it's nice to always come across other Zulip fans, because we are somewhat few and somewhat far between, but relatively happy.

Richard Feldman:

Oh, yeah.

Jerod Santo:

Cool, Richard. Well, this has been awesome. I'm excited about Roc. I generally do leave these conversations excited about what I'm talking about, because that's just the way it works... That being said, there's a lot to be attracted to here, and I do want to give it a shot. Maybe I'll build a little app server or something and see how it goes. Should I wait till Advent of Code? I mean, is it worth waiting so I can get that nice syntax sugar, or should I just dive right in?

Richard Feldman:

Up to you. I mean, since stuff's going to change between now and then, I would probably default to saying wait till Advent of Code. But hey, I mean, nothing's stopping you from trying out what it is right now. Certainly, the current compiler is way more feature complete. I would also note that a common thing that people talk about being interested in when it comes to developing a language is "Hey, how do I get started on building a real thing?" I don't know if you personally are interested in that, because obviously, you're interested in languages... I don't know if you're interested in implementing languages, but if anyone listening is, we also a lot of experience in ramping up new contributors to the language, to getting involved and like "Hey, I want to actually make something in the time checker happen." There's actual opportunities to do that right now, whereas that won't be true once we get to 0.1.0. It'll be a lot more -- I don't know, the number of beginner-friendly projects will taper off.

Jerod Santo:

Okay, so if I want to do that, is Zulip the answer there, or is there a GitHub?

Richard Feldman:

A hundred percent.

Jerod Santo:

Yeah, Zulip.

Richard Feldman:

Yeah, we have a beginners channel, and there's even introductions in there, and usually people will just hop in the introductions and be like "Hi, I'm \_your name here\_" and then "Here's what I'm interested in. I'm excited about this or that." And we can chat from there.

Jerod Santo:

Super-cool. The website, roc-lang.org. What's the website for your podcast?

Richard Feldman:

Oh, softwareunscripted.com.

Jerod Santo:

There you go.

Richard Feldman:

It's also on all the different podcast places.

Jerod Santo:

Wherever you get your podcasts.

Richard Feldman:

Yeah. And we're on YouTube now, too.

Jerod Santo:

Oh, cool. Well, Richard, it's always a pleasure. It's nice to catch up with you. It's been, I think, a year or two we were together at the last Strange Loop...

Richard Feldman:

I remember that, yeah. In person.

Jerod Santo:

Yeah.

Richard Feldman:

Strange Loop, so great.

Jerod Santo:

There was rumors of things that were going to arise, and - not replace Strange Loop, but kind of like carry on the spirit. Are there any events that have done that, or that you know about?

Richard Feldman:

No, I haven't -- I remember hearing at that conference, there were some rumblings about it, but I don't think anyone actually did it.

Jerod Santo:

Yeah, I agree. That's what I've got going on, too. I thought maybe you might know more than I do, being even more of a language nerd than I am. I just think they're interesting; you build these things, so... That's a shame. Somebody needs to go out there and do something, at least in the spirit of Strange Loop, because it was such a great time.

Richard Feldman:

It really was, yeah.

Jerod Santo:

Oh, well, c'est la vie. Some of the best things in life... They have a beginning, a middle and an end... And we just look back at them fondly, and... There's nothing wrong with that, I guess.

Richard Feldman:

Totally.

Jerod Santo:

Well, thanks again, Richard. Awesome time. Looking forward to Roc. Check it out, listeners. Check the show notes for all the things, and we'll talk to you on the next one. See you!

Richard Feldman:

Awesome. Looking forward to it. Thanks!