 Okay, this is Pushing Boundaries with Rock. I'm Richard Bellman. So here I'm going to be talking about the Rock programming language. This is quite a new programming language. If you're not familiar with it, don't worry, you'll sort of pick up a little bit along the way and feel free to ask questions at the end about it. This is very much a work in progress. This is not a language you should run out and build your business around yet. We're working really hard on it and we've made a lot of progress and I'm really excited to demo some of that progress today, but it's definitely not there yet. We don't even have a numbered release yet. I'm going to be comparing Rock to two other programming languages here. For the beginning of the talk, this is going to be comparison of Rock to JavaScript and Rust. We're going to be building the same application in all three, but like most of the industry, I'm pretty quickly going to transition from JavaScript to TypeScript. But it is important that TypeScript does compile down to a dynamic language because one of the things I want to talk about is some of the things that gets you and some of the things that costs you in comparison to something like Rust, which does not have dynamic types. So let's start by just talking about like some trade-offs between these languages, some things that are just sort of like objectively true at a high level. So does the language have runtime type information? In other words, like at runtime, can you have like types that sort of change, like values that change their types? In TypeScript, the answer is yes, you can do that. That's the thing you can do because it's backed by JavaScript, which is a dynamic language. In Rust, that's not the case. Like all information about types that you have at Rust has to be available at compile time. You're not allowed to sort of like change the type of value at runtime unless you're like explicitly wrapping it in like a discriminated union or some type or whatever you want to call it, algebraic data type, one of those. Rock is like Rust in that we also don't carry around runtime type information. This is important because Rock like Rust has a goal of running really fast. Rust is definitely the fastest of these three languages. It's a systems level language with automatic memory management, but also some degree of manual memory management that you can do. Rock, like TypeScript is a language with automatic memory management, but it is not a language that has runtime type information for the same reason that Rust doesn't have it, which is that there's a runtime cost to that and Rock wants to go really fast. Rock is a lot closer to Rust in terms of performance than it is to TypeScript, it runs quite fast. In terms of sound, this is the type system. This is something I'll sort of like explain a little bit more later on. TypeScript famously has an unsound type system. This is an intentional choice that they made as a design choice. I personally think it was the correct choice for TypeScript. I don't think TypeScript would have been nearly as successful if they'd chosen to give it a sound type system, but it does have drawbacks, which we'll see later. An unsound type system means that it is possible in TypeScript to have a program which the type checker accepts and says there are no type problems with this. And yet when you run the program in practice, it turns out there are type problems and you get a runtime error, a runtime type error. This can happen in TypeScript, this cannot happen in Rust and this cannot happen in Rock because both of them have sound type systems. Can you run your program if it still has type errors? So in TypeScript, the answer is yes. Like if the TypeScript compiler says, hey, does a type mismatch here, you can still run the program because it can still compile to JavaScript and JavaScript's a dynamic language so you can still run your program. You don't have to be blocked by the fact that there are still type errors. You still have access to different workflows like for example, running some of your tests but not all of them because even though you have type errors. In Rust, that's not the case. If you have any type errors anywhere in your program, you cannot run anything. You can't run your program, you can't run your tests, you can do nothing until you fix all of those type errors. In Rock, it's actually not the case that you're blocked by type errors. So even though Rock is not a dynamic language it doesn't compile to a dynamic language. In fact, Rock compiles to machine code just like Rust does. Like there's no VM involved. It also compiles to WebAssembly like Rust does. You can still run your Rock program even if there are type mismatches, naming errors, whatever else, the compiler will tell you about them but it'll let you run the program anyway and it'll get as far as it can until it hits one of those and we're gonna demo that a little bit later on. Finally, does the language require type annotations? In other words, I'm not saying does it ever require, sorry, I am saying does it ever require type annotations as opposed to does it always require type annotations? All three of these languages have some degree of type inference. The question is basically, does the compiler ever tell you, hey, I need you to annotate this type because I couldn't infer what it was. You have to write an annotation here. In TriScript, the answer is yes, there are sometimes and in fact there's one that comes up in this application where you have to add a type because otherwise the compiler does not know what the type is. Same thing in Rust, this can definitely happen. There are some cases also comes up in this app in Rust where you have to annotate the type. In Rock, this does not come up. We actually do not have a compiler error that says you need to add a type annotation to this because the type inference is 100%. It's what's called principle decidable type inference which means that not only can the compiler always infer the types of everything in your program even if you don't give it any annotations but also it always infers the most general possible type which means that if you do choose to add an annotation that annotation can never make the type more flexible that's actually impossible than what it inferred. All it can do is if you want, you can add an annotation that restricts it more but you can't add an annotation to make it more flexible because it always infers the most flexible, most general type that would work in that situation. So this is despite the fact that a TypeScript is a gradually typed language, arguably from that perspective, Rock's type system is less restrictive in terms of like your annotation for than even TypeScript being a gradually typed language. So these are all objective facts about these languages. You can observe these, they're empirical but you'll sort of like be able to get a sense for like how different languages feel and like the trade-offs of the ergonomics as we get into the specific code. Now granted, you may not be familiar with all three of these languages and that's totally fine. This is not gonna be a full language tutorial for any of these languages including Rock. Really basically what I'm gonna do is I'm just gonna sort of share enough context as we go so that you can sort of like vaguely follow along with the code samples but don't worry, there's not gonna be a quiz at the end, you don't need to know exactly what every single line of code is doing. I'm gonna be explaining sort of the gist of what we're doing and I'm gonna be pausing and using the code examples as a way to just like give you a comparison point for some of the different things that I wanna compare and contrast between these languages so you can sort of understand better like where Rock's coming from and some of the trade-offs that it is. Also, if you wanna learn more about Rock, this website is like, you can actually get access to like a more comprehensive set of learning materials there. Okay, let me talk briefly about this demo that we'll be building up in all three of these languages. So our demo app is gonna do some basic bread and butter stuff that you typically find in pretty much any kind of application that you're building. Step one, it's gonna read some data from a particular data source and it's gonna transform that data and finally it's gonna put that data somewhere else. So really basic stuff, typical kind of stuff that you'll see in any kind of production application. To get a little bit more specific, the app is gonna be called Movies with an exclamation point. You'll see why there's an exclamation point in a bit. Let's just start by talking a little bit more specifically about what it's gonna do in terms of data reading. So it's gonna do an HTTP get request to a server which returns data in kind of a funky, unusual format. It is a piped limited data. So basically, unlike CSV or JSON or something like that, it gives you back these lines and then each line has a pipe in between the different fields of the line. This might seem contrived, but I've actually at work, well, at a past job, worked at a place that actually dealt with this, a data format pretty similar to this where you had to pipe the limited lines. So the first field in the line is gonna be the title of the movie. So this is data about movies. Second field is gonna be the year and then finally we have the cast of the movie. This is like some actors that are in the movie. So there's multiple lines. So each of these are like delimited by new lines. The cast is delimited by commas. So another line might be the movie airplane, 1980, and then another set of cast members. Okay, so now we've got our data and we're gonna transform it. So we're gonna start by taking the title of the movie. So for example, Caddyshack, and we're gonna make sure that it ends in an exclamation point. But, and this is where we're gonna get a little bit of a business logic that we can write tests around. This is an edge case here. If it already ends in an exclamation point, like the movie airplane, then we don't add a second one. We just wanna make sure it ends in an exclamation point, but it's not required to add in all cases. Okay, so that's gonna be our first transformation. Second transformation is we're gonna take this cast list and we're gonna whittle it down to just the person who's starring in the movie, which we're gonna assume is the first person in the list. So basically take the first person out of the list and then return that. So this is sort of our standing for business logic. This just gives us a little bit of something we can sort of write tests against. It's pretty straightforward. So whatever the list is, just take the first person out and that's the second part of our business logic. Now you can imagine, obviously, like a bigger app, we do a lot more data transformation, a lot more IO, but for this one, pretty basic stuff. Finally, we're gonna write it somewhere. In our case, we're gonna write it to a file called out.json, it's gonna be in JSON format. So we've started with this sort of weird, pipe-delimited input format with a little transformation and we're gonna write it out to JSON. The only final requirement that we have is we're gonna only include movies in the output before 1988. Sometimes you don't wanna write everything to disk or write everything to your storage because maybe it would take up too much space, something like that. This is sort of our final little bit of logic. All right, so read data, transform it, put it somewhere. Specifically, we're reading data from HTTP or transforming it with this exclaimed function that makes sure it ends in an exclamation point and then we're gonna write it to out.json. That's gonna be our app. All right, so let's look through some code examples. We'll start with the business logic because this is sort of the simplest thing here. We're gonna do this little exclaimed function that makes sure that the title of the movie ends in an exclamation point. So as a reminder, we need to add an exclamation point if it didn't have one, but if it already has one, we're gonna keep it the same and not do anything to it. So here's that implementation in JavaScript. Say constexlame, give it the title. You just check to see if the title ends with exclamation point. If so, then we return it as is. Otherwise, we add an exclamation point. I could have also written this using string interpolation. JavaScript has that now, but I decided to do it with a plus. Either way, it works. The TypeScript version looks very, very similar to the JavaScript version. In fact, the only difference is that the title now has a colon string and then it returns a string from the end there. Otherwise, in terms of the actual logic, it's exactly the same. Here's the Rust version. This actually looks a little bit different because although you could write it like this in Rust with like returning the title and modifying it like this, the idiomatic way to write this in Rust, like if I were writing a Rust program, I would do this by having title except the mutable string and then mutating it in place. So slight difference there. And then the rock version looks a lot more like the JavaScript version. String dot ends with title and then title else and then this is rock string interpolation syntax. Okay, so pretty basic function looks about the same in all three languages. Let's say we wanna write a test for this. So here the JavaScript or TypeScript version, I would write in just, let's say I just happened to use that for this example. This is basically a test saying exclaim as a exclamation mark if necessary. If they expect exclaim of ha to be ha with an exclamation mark because there should be one there. It should get added. And then we can test the edge case as well, which is if it already has one, then it shouldn't change. In Rust, we don't actually need a third party test runner. We can just do cargo tests. So cargo is like the Rust build system. We don't need to download and install something separate. Within that, you can actually write these tests right in line. So with just, you need to actually put them in a separate file, but Rust gives you the ability to in the same file as the code, write your tests right in there, right alongside the implementation. You just add this little bang test thing or sorry, hash test thing at the beginning there and that'll be your test. Rock similarly to Rust also has a built-in test runner. So you can just say rock test. It's like part of the language. And in fact, there's just an expect keyword in the language, which is for writing tests. So you can just write the same exact logic as they expect, exclaim high equals ha or without the exclamation point that it adds it. If you don't have to, but if you want to, you can add like a little description to your test, not by putting a string or anything like that or naming a function, but rather just by putting a comment above it. And if you do that comment will appear when the expectation fails. I'll demo that a little bit later. Okay, so that was our first basic function. Let's get a little bit more complicated in terms of our implementation with handling errors. So you may recall that these are the lines we get back from our HTTP requests. They're piped to limited and then the cast is common to limited within that. So we're gonna write a little bit of logic to sort of like split one of these apart. So to do this, we're gonna write a type definition. So in TypeScript, we're gonna write interface movie and it's gonna say we have a title field, which is a string, a year, which is a number and a cast, which is an array of strings. The Rust version looks pretty similar, title string. We are gonna have a year via U16, which is an unsigned 16-bit integer. This is more specific than number in a number of ways. Rust has like a lot of different numeric types whereas TypeScript doesn't have as many. And then for the cast of the movie, we're gonna have a back of strings because that's the sort of analogous type that you would use in Rust. One thing you see in Rust that you don't see in TypeScript is this derive across the top. That's just something you need to do for testing. It's not strictly necessary in every application, but for this particular case, since we're writing tests around this, we do need to actually do that. The Rock version probably looks more similar to the TypeScript version, but you do see, we also have like similar to Rust, like a lot more numeric types. And so we're also using a U16 here for the year. One thing to note though, is that in the Rock version of this, this is optional. Like we could make this whole program work and not write this type declaration out. I'm just doing it to make the code a little bit easier to read, like more self-documenting when we're talking about types. In the Rust version though, this is required. You cannot write this Rust program successfully without having this declared. And actually as it turns out in the TypeScript version, that's also the case. If you don't have this, there's one particular part of the code which we'll see later, where there's a filter call where this actually will not type check. The TypeScript compiler won't understand it unless you add an annotation saying movie is and then the name of its interface. So there's actually no way to type this TypeScript program without writing out the interface. But in Rock, again, all type annotations are optional. So you don't have to write this if you don't want to. Looking at the specific implementation, here's what the TypeScript one looks like. So we're doing a line dot split around the pipe. So it's splitting it into three different pieces, the title, the year and the cast. And then we're calling exclaim on the title. That was that exclaim function we wrote a moment ago which is gonna make sure it ends in an exclamation point. For the year, we're gonna call parse int to translate the year from a string literal into an integer, which we need to do because later on, we're gonna do that check to see if it's less than 1980. Just decide if we want to write it to out.json. And then finally, we're splitting the cast around commas so that we can get the first cast member out of there. Now, this program as written, like I did a stock TypeScript install and did a type check on this. And it was like, cool, no problem. This is, you're all set, nothing to worry about. And then when I wrote a test that tested some invalid input that didn't have like all three fields in the pipe delimited string, it crashed. It had a type error. This is the output of the test. But this type error happened at runtime. So this is a runtime type mismatch. This is an example of TypeScript's unsound type system. I ran the type checker. It said everything looks good. And then when I actually ran the test, it crashed at runtime with a type mismatch. Specifically, in this case, the type mismatch was that we ended up with undefined here and I was trying to call a split on undefined. I see that they don't call it undefined as not a function anymore because that was so infamous, but it's the same error. It's just a different error message. We had an undefined and I tried to call function on it. Fortunately, as it happens, TypeScript is configurable so I can actually make it so that this is a compile time error. So if you go into your tsconfig.json, there's a flag you can enable that says add undefined to a type when accessed using an index. So I enabled this. I actually enabled every single one of the optional things. So for the rest of the talk, all the TypeScript stuff I'm talking about is basically using all of the maximum like TypeScript type checking settings like everything's enabled. So it shouldn't have a minimal amount of unsoundness compared to like a stock insult. So it's the least unsoundness you can get. So once I did that, I did actually get a compile time error for this. In fact, I got three for like three different places where this was going to be a problem. However, although I was able to resolve those errors pretty quickly by just adding this right here, just adding these equals equals null checks, equals equals null in JavaScript checks for both null and undefined and just doing an early return for null. Oh, which by the way, that does mean that I need to say that this now returns either movie or null because now it's possible for it to return null. So I've handled this. However, I still got a bug in the test, which is that if I tried giving it a line where the year was a string rather than a number, it didn't early return to null. It didn't catch that and say, oh, yes, this whole thing should be null but we're going to like go on the error path here. But rather it just happily set year to be NAN, not a number because unfortunately that's just what parsing does in JavaScript. So this is not an example of unsoundness. It's just an example of API design. Parsing is just defined to happily accept anything that is or is not a number. And if it gets something back that is not parsable as a number, it just returns NAN. But that's not really what we want here. So I just added one more little check here to parse the int if check if it's not a number and if so return null. This still doesn't actually catch all of the possible problems because another thing that parsing does is if it gets something that does not parse as an integer but which does parse as a decimal, it'll just silently discard all the decimal places. So really if I wanted to get like extra careful about this, I could parse it as like a decimal first and then try to translate that into an integer. I decided to kind of draw the line here and not go further. Interesting point about like API design and the relationship with like type soundness. From my perspective, I just wanna know is this thing an integer? If it's not parsable as an integer, I wanna error out but that's like actually kind of surprisingly hard to do here. Of course, there's probably third party libraries that I could install to do that. Okay, here's the Rust version. I'm not gonna go through like all the stuff that this is doing. One notable thing is that we have an iterator here. So I'm calling fields.next to sort of advance through the field rather than that sort of like destructuring syntax that I had in TypeScript. With the Rust version, when I tried to compile this, it did actually give me type mismatches for the cases where I basically like wasn't handling the possibility of things going wrong. So this is talking about this option. So option is similar to like maybe and Haskell or Elm, especially a wrapper around a particular value in this case of string saying that like, I might or might not have a string there. You can kind of think of it as like a nullable equivalent if you're not familiar with maybe or option in other languages. And again, this is because Rust does have a type system. I did get this error at compile time. I was not able to run the test and like have the test crash at runtime of the type mismatch like I was with a stock TypeScript install. So here are the sort of like offending problems. These are the calls that needed to have the possibility of failure checked that I don't have checked here. In Rust, there was a very, very concise way to handle these errors, which I'm gonna show when I go back and forth between these. It's just, you have question marks here. So basically all three of these spots I just add a little question mark at the end. And now basically what this does is it's similar to what I did by hand in the TypeScript version. The question mark operator essentially says if this thing is a none, which is kind of like the option equivalent of like a null just early return this function just like immediately bail out and return none for the whole thing, stop executing the rest of the function. If I'm gonna do this, this means like to use the question mark operator like this, I do need to wrap my return value in a sum and then I do need to change the return type of the function to be an option of movie instead of just a movie. So again, similar like the return type has to change if I'm now potentially returning an error case here, which is fine, that's sort of what I want. Okay, here's the initial rock version. So similar to the Rust version, we also have error handling the, we actually use results rather than option but same basic idea, like all of these list dot gets that are pulling out, you know, fields zero, one and two from the list. Those can all fail, but in this code I've written that failure case is not actually handled. Here's the error message I get in the rock compiler type mismatch, this first argument to exclaim has an unexpected type. This get call produces a result and results is kind of like option except it's got both a success type and a failure type. So here it's saying the success type is a string. The failure type is just saying I was out of bounds. Like in other words, this whatever index I gave to this list was like too high for how long that list is. So it was an index out of bounds. So it says exclaim needs the argument to be string because you may recall we wrote that exclaim function that's just takes a string and returns a string. So I need to like fix this problem somehow. One difference between this rock error and the Rust error that we got is that there's a little bit extra down at the end here. Oh yeah, I get this because it is a sound type system like Rusts, but the extra bit at the end is it says you can run the program anyway with rock run. So even though I got this tightness match the compiler is like, I'm gonna tell you about this but I'm not gonna block you from running the program or running tests. So like in the Rust version because I got that compile time error it's not possible for me to run my test suite. And like, even if there are parts of my code that don't even hit this code path I can't run any of them until I fix all my type errors but in rock I actually can't. It'll tell me about the type error and it's a sound type system. So it will catch a hundred percent of those. It will never have a runtime tightness match and yet I can still run if I choose to. And then of course, like if it gets here then it will actually have to like bail out and crash. But that's not because like the type systems unsound it's because I'm sort of opting into it. I'm saying like, hey, just go ahead and run anyway because for whatever workflow I'm in maybe I want that. Okay, so this is the version in rock with error handling. Rock doesn't have a question mark operator like Rusts said we just use plain old function composition. So I'm using result.map, result.try and result.map error here to sort of like, massage my errors through the program. One thing I wanna call out here is that I'm also doing this thing at this last line here. I didn't need to do this strictly speaking. Result.map error basically like translate all of these different potential errors into just one sort of catch all error that says invalid line and then stores the line in it. The reason I did this and I'm gonna show you a little bit later what this sort of gets me is really for the benefit of the type annotation which again is optional. I don't want to, I don't have to annotate this program. All of this code that you see on the screen works completely if there are no type annotations in the program. However, I do like to annotate my types. So if I decide to add a type annotation to this function it'll tell me, okay, this returns a either a movie like if it's succeeded or now it will say this could return an invalid line with a string inside of it namely the line that was invalid. And we're gonna see later how errors actually accumulate in rock automatically such that I can end up handling this invalid line error at the end of my program just like right alongside all of the other types of different errors that I'm handling. So I don't need to do this but I'm doing it so that it makes my life a little bit easier for my future self. So I don't have to think about all these different error types that all these different things that can go wrong. I just have one invalid line and that's it. Okay, zooming out. Here's what all of these three implementations look like sort of side by side. Honestly, they're all like pretty similar lengths like the TypeScript one is a little bit more vertical like the lines aren't quite as long but there's more of them. But really like at the end of the day it seems to take about the same amount of code to do like sort of like the validation for all of these. And by and large for most of this application that's kind of what I felt is that none of these languages are like significantly more or less concise than the others. Okay, loading data. As previously mentioned, we're gonna do an HTTP get in order to get our lines of piped limited content. Here's what that looks like in each of these languages. I'm gonna sort of like gloss over things a little bit more and not get so much in the implementation details from this point on. So I'm just gonna kind of call out and sort of like zoom in on some different parts of the implementations here that are sort of interesting. So one thing is all three of them use the word await. So these are all asynchronous HTTP fetches. So here's the TypeScript one we say await fetch URL and then we say await response.text which is basically like, you know, you get the response back and I want to just get the text out of the body. So you call await on both of those. Same thing in Rust except the syntax is a little bit different. You have dot await instead of await space. So same basic idea. There's that question mark again to automatically have the errors like early return if it hits an error in rock. We don't have a separate keyword for this. The language doesn't have like a built-in notion of async rather this is just a plain old function called task dot await. Comparing the three type signatures of these functions is like get movies that does the HTTP request. We can notice a couple of similarities and some differences. So one similarity is they all take a URL as an argument in the rock one rather than taking a string I actually made like an opaque URL type. That could be done in any of the languages. I just like, you know, their APIs just happen to not do that, but they could or the rock one could take a string but this is just like the design they happen to go with. The Rust one returns a result and has this async keyword in front of it. So async keyword in Rust basically means that although this says it's returning a result actually it's returning a future of a result. The future is just like not shown in the type but rather it's sort of like implied by the fact that you use the async keyword. So this is a side effecting function. If you call get movies it will immediately run that HTTP request as soon as you call that function then it'll return a result. TypeScript version, you also have the async keyword but that doesn't actually affect the return type in TypeScript. This says it returns a promise and that is actually the type that it returns. A promise in TypeScript is chainable so you can chain promises together but this is still a side effecting function. When I call get movies in TypeScript it's gonna run this right away. It's gonna return a promise as well but it is immediately gonna do that HTTP request. So in both of these cases if I call get movies a hundred times I'm gonna get a hundred HTTP requests right away. Async or no. And rock things are a little bit different. So in rock this returns a task there is no async keyword. Again rock doesn't have a language level concept of like async or await. It's all just done with functions and values. Also task is unlike promise and unlike futures and results in the Rust version. Task is a description of what I want done. So rock is a pure functional language. When I call get movies in rock nothing happens. It just returns this task and it's only after I've built up a long chain of tasks and handed them off to main that things actually get run. So if I call get movies a hundred times in a row zero HTTP requests happen. It's only after I've sort of composed together these task values and handed them off to main that they will actually do any HTTP at all. So this is kind of similar to like it's actually the type is also called task in Elm. IO and Haskell different languages is called different things. Rock happens to call it task in some part because rock is a direct send into Elm but also because that just seemed like a nice work for this. Okay. Each of them has in the type signature what do they produce if the HTTP request succeeds. So all of them are basically saying I return a collection of movies. That's a vacant Russ and array and typescript and list and rock but same basic idea just like a bunch of movies. The way that it obtains these is by calling our movie from line function. So the Russ one can fail. It can fail in a variety of different ways but in this particular case we're just annotating it with this sort of generic catch all like, hey, this is something that could fail error type. There are lots of different ways to do sort of composable error handling in Russ different libraries that will like give you some more specific type in here. But I decided not to sort of open that can of worms for this talk because this isn't really a Russ talk. So just be aware that like in this talk the Russ error type is just gonna be this generic something went wrong type but if you were actually writing a Russ program one way or another you would have a more specific type I just didn't want to open that can of worms. In the rock type there is one standard way to do this and it's not with a separate library. It is just like something using just like ordinary language features. And basically this is how it looks. What this type is saying is that the way that this particular task can fail is you can have an HTTP error or you can have this custom invalid line error that comes from our movie from line function that we wrote earlier. You may remember we made that custom like on the fly in the line type. So this is basically saying either of these two errors could happen when you run this task. And we'll see how those errors get handled later on. Type script to promises do not actually specify the error type. So there's no way to say that like, oh, this thing could fail with an HTTP error or it could fail with like a file IO error. We just don't get the information about that. You just have to handle all the errors at runtime based on like what they turn out to be. There's no way to like I'd compile time specify those using a promise. And finally, we have one last type variable here. This is a rock specific one. This doesn't appear in either of the other two languages. And this is the effect type. So this is basically saying this particular task does network IO. It can talk to the network. This is notable for not just what it says, namely that this task does network IO but also for what it doesn't say. So because this only says network IO, I know that for example, this task is not even capable of reading from or writing to the file system. It's just not, it only does network requests. There's also no like CFFI in rock that would let you like cheat and like sneak around that like, oh yeah. You know, it says it doesn't, but actually it does. No, no, no, there is no cheating this. If this says it only does network requests, it really only does network requests like guaranteed. And we're gonna see how that can be useful later on. Okay, which brings us to part four, writing data. So we've now gotten our data from HTTP. We've translated it into like nice records. And now we're gonna write it out to out.json with the restriction that we're only supposed to include in out.json movies before 1980. Also, you may note that we didn't actually implement this one earlier. So this is where I'm gonna kind of throw in that final requirement of translating the cast into just the starring like the first person in there. So we're gonna do that in this part sort of at the last minute as well. Okay, here are all the implementations in all three different languages of doing this. So this is like this function is called write output. I wanna call out some things here. First of all, this is the only case where the rust one is like kind of significantly longer than the others. And we'll see that it's pretty understandable why that is pretty quickly here. So in the TypeScript version, when we are building up our JSON that we wanna write to out.json, I can just do that with an object literal. I don't need to name it. I don't need to do anything else. I'll just write curly race. And then the title of the row that we're writing to this JSON is movie.title. And starring, I don't need to write starring colon starring because I already had a variable name starring which pulls out the first one in the list. This is just like syntax that's equivalent to syntax sugar for starring colon starring. So that's it. That's how I get my record. And then when I wanna serialize it, I just say JSON.stringify on all of my records that I built up each one of those records looks like this object literal. And that's it. That's the whole serialization process of in TypeScript writing this data out to JSON. And I pass that to like a write to disk function. Now, in contrast and rust, you don't actually have anonymous like object literals like this. If you wanna have something with named fields, you have to name it and actually write out what its types are. So here I made one called JSON record. This is what it looks like to construct it, but I actually have to separately write out struct JSON record and explicitly say what its field names are and what the types are of those. You might also notice this little deriving line. This is a different deriving line than what we saw before. In particular, I wanna call out serialize here. So this comes from a third party package called Serity. This is a very, very popular package in the Rust world. Serity is short for serialized, deserialized. And basically it's a way to take your types and generate encodings and decodings of those to various different serialization formats just based on the types. So for example, in this particular case, this allows me to write Serity JSON, which is like sort of the JSON flavor of Serity to VEC, which is converting this to like a VEC of bytes, passing in my records. So this line is basically the same amount of conciseness as JSON.stringify, but I did have to like actually define JSON record separately unlike in the TypeScript version in order to get access to this sort of JSON.stringify like thing. One thing that's cool about Serity though, is that this works for anything, not just JSON. So if I wanted to, I could swap out Serity JSON for Serity CSV or like a binary encoding. And I would still just write this one line of code to do that now that I annotated this serialized, whereas in TypeScript, JSON.stringify is baked in, but you don't necessarily have the same equivalent with like CSV or other formats like that. Although granted, you can probably get those off the shelf and still have it be about as concise because JSON.stringify can work on runtime types. Serity's deserialized actually works quite differently than JavaScript's JSON.parse because Serity's deserialized, when you write the equivalent of this except going in the other direction where you're like taking some data and deserializing it into your type, it actually does all the validation right there immediately. Whereas like JSON.parse in TypeScript, we'll just sort of take your data, turn it into a JavaScript object and not validate any of it at all. It'll just sort of like hopefully it validated, I don't know. Again, there are like third party things you can get that will like do those validations, but it's pretty nice that with Serity, you get that like serialized and deserialized with like validations upfront and without having to have runtime type information. Okay, what about the rock version? So in rock, unlike in Rust, you do not actually have to annotate your types, you don't have to name them, you can just do literally the exact same syntax as the JavaScript literal. You can just say title, cool movie.title and starring and that's it. And that's how you make your JSON record. How do you serialize it to JSON? Again, almost exactly like the JavaScript version. I just call encode.2bytes passing my records. And then there is one more argument here which is JSON.2ETF8. And this is basically like Serity where I give it this encode.2bytes as a sort of generic function. I can give it any value that supports being serialized. And then I give it the format I wanna serialize and do in this case, JSON. But if I wanted to go to CSV, I just give it a different argument here. Now you might be wondering how we're doing this given that unlike TypeScript, like we don't have runtime type information, how can we have something that's this generic? And this is actually, this feature in rock is directly inspired by Serity, literally. Basically what we did is we took this thing and instead of making it something that you have to get from a third party library, we've made it part of the standard library. Rock standard library just includes this concept of encoding and you don't have to write out the derived either because we have full type inference and we have structural types like this instead of nominal types where you have to name them. We just know how to derive from any arbitrary record that you write, how to serialize it. And in the future, how did these serialize it? We're working on that too. So basically you can just say like, hey, turn this into JSON and they'll say, cool, I know how to do that. Now, of course, if you wanna do some like more exotic encoding, which is something that definitely comes up, then you can write like a hand serializer. That's totally an option too. But in a lot of cases, when you're working with like data where I just wanna just write it out to the disk and sort of the obvious format, this is a really handy time saver. And again, we're able to do this without having to pay the performance cost of runtime type information. So Rust and Rock using compile time types, typescript using runtime types. So basically I would claim that these are basically equivalent in terms of ergonomics. In fact, I actually like this version a little bit better because I can just pass in as that second argument, a different encoding just to swap it out. Whereas with the typescript version, I might have to go to a totally different library that might have a totally different API to it. So I get sort of like the ergonomics of Serdi from the perspective of like swapability in different formats, but I get the ergonomics of JavaScript and typescript with regards to not having to actually like name my types up front. It's all just done using type inputs. As far as I know, it's a language that's done this and we just got it working. So I'm pretty excited about it. Okay, so comparing the type signatures of our earlier get movies function and this new write output function, couple of things I wanna highlight. So in the Rust version, they both return results. So the success type is like a back of movies for get movies like we saw earlier. The success type of write output is basically like nothing. So nothing interesting there. Again, with the error types, I decided to just go with the most generic error type. So there's no extra information here, but in like a real world Rust project, you would certainly have these have different errors. It's just that combining those together is like kind of a pain. Otherwise, if you're not using a third party library and I didn't wanna go down that rabbit hole. TypeScript version, it says promise of a movie with, sorry, an array of movies or promise void, which means this promise doesn't produce anything. Again, TypeScript does not tell you anything about the error types. In Rock, we have either list of movie or empty record, which is our equivalent of like, this doesn't produce anything. But for the error types, we actually do see different error types here. So you may recall, get movies can either give us an HTTP error or an invalid line, which is that custom error type that we specified. Write output doesn't have either of those, but it does have a file write error because if you're writing to disk, you can have something like, there's a permissions issue, like the disk is full, bunch of things could go wrong. And that's captured by our file write error here. Finally, we have our effect type. So in the case of get movies, we can see that it only does network effects. And in the case of write output, it only writes to disk, it does nothing else. So it doesn't read from disk. It doesn't do network stuff. All it does is write to disk. And I can tell that just from looking at the type, even if I don't show you the implementation. Okay. And finally, we're gonna get to the actual demo. This is tying all of that stuff together. So let me just switch over to my editor here. Okay, I'm not gonna bother going through the Rust and TypeScript versions because I think that the comparison part is sort of over. From here on out, I just wanna talk about like Rock, and then I'm just gonna kind of wrap up and summarize everything. I'm actually gonna run some of this code. So here is my movies implementation. You may recall this is like very first, one of the very first things we did was to find this movie type. Title is a string, year is U16, cast is a list of strings. This is that first function we wrote exclaim, takes a string, returns a string. Here's our movie from line, takes a string returns a result of movie and invalid line. I did for the demo, I added one extra little conditional here. This is not strictly necessary, but you'll see why this is a part of the demo later on. Basically I just kind of special case, like hey, if we happen to have an empty string here, just immediately return invalid line, don't bother doing it, splitting or any of that. Like even if I don't have this, it will still handle the empty line case correctly. I just added it specifically because I wanted to add an extra conditional here. And again, that's just so I can demo something else later on. So I'll bring that up again when I get to it. Here's our get movies function, takes a URL, returns a task that produces either a list of movies or if it fails an HTTP error or an invalid line, that invalid line being that custom error type that we defined up here with result.mapError. And finally the effect type is this does network access. Okay, write output. This is the last of the functions we've already seen. Takes a list of movies, returns a task that produces no output, but it can produce a file write error and the effect type is write to disk. So this is, you know, out.json. Then finally we have the encode.toBytes. This is like our kind of equivalent of like json.strengify and then we just pass that to file.write. Okay, so as you can see at the bottom of the screen, I haven't quite scrolled down yet to main, but I wanna notice some interesting things about main that you can tell just from the type signature. So main does produce a task. It has no output type here. So it doesn't produce anything. It has no error type either, which means main cannot fail, which must mean that we've handled all these errors that we've been dealing with. At some point in main, I can tell that just from the type. And also I can tell that main does more IO than any of these other things do. So check it out. Not only does main write to disk, but it also writes to standard out and to standard error. And now you can sort of infer something else from the fact that these types are appearing in main type, which is that none of these other functions that I called wrote to standard out or to standard error. Because if they did, I would have been able to tell that from the type. We can see that main does do network stuff and also end, which is like it gets environment variables. That's another nice thing. So I can now tell because end was listed as a effect that none of these other functions depend on environment variables. They're not gonna like magically work different if an environment variable is set. I know that for sure, because they don't have the end type. When I first introduced this concept, like I did my first little like refactor of a program, a rock program that like I had this, I was actually really pleased to find this because I had some functions that were doing like standard out and standard error and end. And actually one of them was doing exit, which was like the effect type for like exiting the program early. And I was looking at their types and it bothered me. I was like, wait, no, this shouldn't need to do that. This shouldn't need to do those things. You know what I should do? I should just like move these into the error type and then like propagate those through and handle those like at the beginning of the program where I'm handling all my other errors. And it was nice because it made those functions more testable, like easier to test because now the error type is like a semantic value as opposed to just like, oh, it just like immediately exits the program. So one cool thing about this is that you can tell all of these types, all this info just from how these functions are used. So because I call this environment variable that give me a UTF-8 environment variable API key. By the way, that's what we're using the environment variable for it goes in the URL to our little server here. Just the fact that I called this cause the environment variable to appear in this effect type. I did not need to say like, oh, by the way, this is like, no, no, just the fact that I awaited this task caused that to appear. And in fact, if I delete this the compiler is going to yell at me and say like, this is, you know, you're missing an effect type here. I'm calling get movies. That was our function from earlier that does the HTTP get. And again, because I call that network has to appear here. Then I write output and because of that, write to disk has to appear here. And where's the standard out and standard error coming from? Well, that's because at the bottom here I'm handling all my errors right here all at once. And I'm handling them with pattern matching. So I'm turning my task in result with task.attempt. And then basically I can pattern match on it and say, okay, if it was okay that means everything's succeeded. And so I can print the standard out. Hey, I wrote the file. If it was an error, I'm doing a nested pattern match here to handle all these different error cases. So the environment error that can happen from this is like, what if the API key is not present in the environment? So then I'll, okay, I'll print the standard error like, hey, I couldn't find it. If there was an HTTP error, I could print just that standard out. Hey, I couldn't read from the URL. I could also, since this is a pattern match I've got a little underscore here saying like I'm not trying to pattern match more on that. But if I wanted to, I could say like, you know what? I'm gonna handle timeout differently and like write something different. And then if it wasn't a timeout, but it was like a not found then I'll print something different. Like totally, totally fine. I can do that. Or I can be more generic like this. Also file write error and dial line. One thing that's really nice about this. So if I go over here and I do rock check on my program. So initially it's like zero errors and zero warnings, oh, 13 milliseconds. It's really slow. All right, I got zoom running. That's why it's so slow. But usually it's like closer to like 10 or fewer milliseconds for a rock check on this program. But we're really serious about compiler performance. But so if I delete one of these, like let's say I'm just like, you know what? I don't care about handling file write errors. Like I think that's never gonna come up. I'm never gonna have any problems with the disk. I'm just gonna delete that, not handle it. Now if I run rock check, it's like, nope. Tighten this match. Like you didn't cover that case, you know, can't happen. But what I can say is if I don't want to handle file write errors, one thing I can do is I can just be like, you know what? I'm just gonna have a catch all here. And like this is gonna say like, ah, you know, blow up. Like that's fine. It'll actually, you know, it'll let me do that and give me no errors as long as I'm actually covering all the cases. And as you can imagine, like just like I can make some of these more specific, I can also just like delete all these and just say like, okay, you know, if there's any error whatsoever, we're just gonna blow up with no additional information. Also, as previously noted, like if I try to delete one of these things, like I'm like, you know what? I'm just, yeah, it does an environment variable, but like, I'm not gonna tell you about it. Nope, nope, nope. It's like, hey, you know, you're missing this environment variable, like type in there. Again, you can't cheat the system. It knows about all the effects that you're doing and they have to be in here. Which means that if I had a much bigger program and I was trying to understand like, which parts of my program are doing which things like bugging purposes or if we're trying to organize my dependencies, I can rely on this to tell which parts of my program are doing what? Not just are they doing effects, which since this is a pure functional language, I can tell you right now, this string to string function does zero effects. This function that returns a result does zero effects. It's not even possible. Only the ones that return tasks are even capable of doing effects. And I can tell you exactly which effects they are doing. This one's not writing to disk. I am quite sure of it because that's not, if it were, it would have appeared in the type. So if I have something where it's like, hey, something is going wrong and like something is getting written to disk surprisingly or it's not working like this function, will this function work if I'm offline? Yes, I'm quite sure of it because it doesn't do any network access. Like this function is sort of offline safe by, and I can tell that just from looking at the type. And again, this is reliable throughout your entire program. If I suddenly call a function inside here that does network, if the compiler is gonna yell at me just like it did a second ago, unless I add that to this type. So I know people talk about like, what color is your function? Like red, blue functions, and they talk about it like it's a downside. Like it's a bad thing if you have to have quote unquote colored functions that like, you have to like wrap this in a task if you wanna do an effect here. I do not consider this a downside. This is a 100% on upside to me. I mean, yes, there's a trade off there. Like yes, if I want to start doing an effect somewhere I have to wrap it in a task. Like if I want this function to start being like affecting I need to change this from result to a task and I need to add an effect type. Good, I want that because that means that these type signatures are telling me so much more. So yes, there is a cost to like having to actually like annotate your types in that way if you wanna get that benefit. But to me, that is absolutely worth just how much information I now get out of having these types signatures. So I wouldn't say that this is like the, the colored function problem. I would say this is the colored function feature at least when you get this level of detail out of your types. Okay. So let me demo some more stuff. So I talked earlier about our testing. So we have like expect, exclaim, high, it was high. So let me just real quick demo, like if I break this test actually, sorry, let me demo running the test successfully first. So here we go, zero fail in the forecast. This did run, take a lot longer to run than rock check. Probably that's because like this is using a slow backend like the LLVM backend. We don't quite have our fast back ends working on Mac OS yet. That is a work in progress that's going to take a while. But on Linux, this would run much faster because we can actually use the development back end at the end of the surgical linker. But anyway, still they all compile the M-RAN in like less than half a second, but it should be a lot faster for future records. At any rate, oh, we do have it. Actually, if I ran web assembly, it would also be really fast because our web assembly backend is actually a feature parity with LLVM, just about. Anyway, let me break one of these. So I'm going to change this, you know, H, right? So that's not going to work. The expectation failure format is basically the same as what we saw for like type errors. So it just tells me like, okay, here's the thing that failed. You might notice that there's no like, you know, like which test is it? So if I want to just be really concise about this, and I just want to like throw these things in there without naming them, I can do that. But also, you know, I do have a line number to help me find it later, like line 86. But I can also say like, you know, H equals equals R or whatever like this is my test, whatever. And now when I run the test, it's actually going to like grab that comment and like include it in the error message. So this is kind of like equivalent to, you know, naming your tests, but it's optional. And of course, if you want to have like a more detailed description of like what the test semantics are supposed to be, you can do that too. Okay. So something that's more interesting to me though is thinking about workflows. So I mentioned this a little bit earlier. What if I want to like run my tests even though there's like a naming error or a tight mismatch in my program. So one thing you can do in rock that I think is pretty cool is oftentimes actually my workflow is not that I want to try the implementation first, but rather I want to try to type first. So sometimes I'll just write out a bunch of type annotations like this and just, you know, not even bother implementing them and just sort of see how they fit together. Like I'll write like one implementation that tries like calling different functions and just writing out the types of those functions and just seeing like asking the type check or hey, do these fit together? In rock we have first class support for this. So even though I deleted the implementation of this function, this actually still works. So if I do rock check on this, it'll be like, yeah, okay, no problem, sure. I believe you that that's the type. Now, since I deleted the implementation, it's not going to work as well, shall we say. So if I do rock test, two of my tests are actually using that function. Those two tests are gonna fail. So it says this expectation crashed while running the crash reported this message. Okay, like I said, rocks will work in progress. This is not the nice stair message. We're working on it. This is gonna improve in the future. But for right now, the relevant part is that it ran as much as it could, but then as soon as it actually tried to run this moving from line function, it was like, okay, it's not implemented. You cannot actually run this function that doesn't have an implementation. But it still did run these two other tests. These just expect exclaimed tests because exclaimed does not depend on moving from line. It doesn't run them at all. So it's like, oh, well, I can still run these tests. Sure. Or one as many tests as I can, except for the ones that like, have like a missing implementation. But what's much cooler than that is, let's, so I put that back. Now rock check will pass and rock test will pass. Now let me introduce a tight mismatch. And I'm gonna actually do it right here. So I'm gonna comment out this like thing that parses the year out of it and turns it into an integer. I'm just gonna say year equals like not an integer. This is a string. This is absolutely a tight mismatch. When you rock check, it's like, yep, tight mismatch, you know, cannot do that. What are you doing? But I can still run my tests. Now what's really interesting about this one is, okay, yes, one of the tests failed, but actually it was one test that failed and not two tests. So I have two different tests here. One of them says, I'm gonna pass in this sort of like, you know, movie from line thing. The other test just passes in an empty string. Now remember that I mentioned earlier, I introduced this extra conditional here that does a special case. If the string you pass is empty, it immediately errors out and doesn't keep running. That's the test that passed because the tight mismatch is down here. It's in the else branch of this conditional. So the program actually still runs movie from line. And in fact, it's even able to run this first conditional and get to here because there's no tight mismatch in here. Everything up to this point is fine. The problem only comes when I get deeper into the function. And so yes, the other test, which actually gets to this line where I have the tight mismatch, that one has to crash and give me an error, but it's still able to run up to that point. So this is really cool. And in fact, it not only is a crash, but it translates into a test failure. So this means that if I want to, I can do a workflow where, and not only can I run it in tests, I can also run it in like actually run the program. So this means from a workflow perspective that I am not blocked by compilation errors, but I always have access to them. Like this is a sound type system. At every case, if I run rock check, it'll be like, yeah, I know about this. You cannot sneak anything past it. If there's a type error, it's going to tell you about it. But just the fact that there's a type error does not mean that the program is blocked from running. I can still run the program. I do want to put a big caveat on this, which is that I'm demoing this. This works. In general, we have this implemented in the compiler. This does not work consistently. There's unfortunately quite a few cases in the compiler where you get a tight mismatch and rock check is like, yep, I know how this tight mismatch, you try to run the program, but instead of like just giving you a nice error like this does or maybe a not so nice error. In some cases, it just fails to generate the program, and the compiler itself just like gives up and crashes. So we're working on that and like making it so that under more circumstances, you can like completely run your program. But basically the way that we do this is like, we detect that there was a tight mismatch here and then we, at code generation time, we're like building the final binary. We just explicitly generate a line of code here that like says crash if you get here. As it turns out though, because this is a like compiled language, this like affects other parts of the program. And so in some cases like the code generator like doesn't know how to deal with that. And so again, something that we're working on rocks the work in progress, et cetera, et cetera. But so if you're downloading the nightly binary and like playing around with it and you're like, hey, I tried this thing and it didn't work. Like you did in the demo, that's probably why. It's just like your works in some cases, like and it really works and it's very cool. But in other cases, it just doesn't work yet. So work in progress. Okay, so let me switch back to the slides real quick to just kind of wrap up here. Just talking about a summary of all the cool stuff we talked about and some of our plans for the future. So at the beginning, I talked about these trade-offs in these different languages. So TypeScript has run-time type information, Rust and Rock do not. This means that Rust and Rock are able to run a lot faster than TypeScript. However, it does mean that some things are more challenging. For example, JSON encoding is one example. So like Rust needs that actual like type annotation and to use Serdi and to like derive those things explicitly based on the type that you wrote down. But as we've seen, you can actually get those same ergonomics in other ways, like the ergonomics of a dynamic language or of like run-time dynamic type information at compile time using type inference, which is exactly what Rock does. So although we don't have run-time type info, we're still able to provide that same ergonomics for like encoding and in the future we hope to do it for decoding as well. TypeScript does not have a sound type system, Rust and Rock both do, but as we saw in the case of Rock, that doesn't mean just that you have, just because you have a sound type system and you're compiling to machine code and without VM, without run-time type information does not mean it's impossible to make a program that runs without, even though you have type mismatches. Haskell actually has a compiler flag you can do. I don't know how robust it is, but it's a dash, dash defer type errors. So you can do some stuff like this in Haskell. But in Rock, we've really designed the compiler from the ground up to be able to always run, even if you have type errors or naming errors, any errors, just try to get as far as it can. Although like I said, that's one of the parts of the language that's like the most work in progress in terms of like being feature complete. And finally, does the language require type annotations? Surprisingly, even though TypeScript is a gradually type language, as we saw throughout the building the application, there are definitely cases where TypeScript actually requires you to have a type annotation or else the type checker just doesn't work it. It's not able to type check your program. Same thing in Rust, but in Rock's case, we do not require type annotations. If we ever do this like, you know, that's a bug, but like by design, everything, all the types can be fully inferred just based on usage. And in fact, the compiler can tell you the type if you're wondering about it. You know, if you just wrote the implementation, you're not sure what the actual type is. So talking about some like future work, this is a CSV parser, a shout out to Martin for writing this. This is like sort of like a handwritten CSV parser where you're like, I'm explicitly stating, like here's all of the like the fields in my CSV, like I'm saying like the first one's a string and then it's a natural number and then like the cast parser, which is defined as this sort of like embedded parser, like doing the string that's split. So this is an example of like, what if we'd gotten the movie info in a CSV format as opposed to this pipe delimited thing? What would that look like? Now it's important to be able to write like manual parsers like this because sometimes, you know, the auto derived parsers are not quite in the format that you want. You wanna be able to have that level of control to be able to write something like this. And in the future, you wanna be able to give you the option to do both. So you could either use Martin's like sort of parser combinator style of decoding like this or you want you to be able to write something like this, where you just say results equals decode.decode, which is similar to the like encode.encode that we demoed earlier. Given the input, which in this case would be like some bytes or a string or something CSV.fromUTF8, which is kind of analogous to the JSON.2UTF8 we saw earlier. And this would give you back a result of either a list of movies because that's what you're parsing here or a decode error. So the encode is just able to, you know, go to like create the like the raw binary format. This is able to go from raw bytes into a type of your choosing. And of course, just like before, we'd like you to be able to do this, you know, not just for CSV, but for JSON for binary encoding any kind of user defined thing you want. You just need to provide one of these formatters and we can automatically infer how to connect that into your into decoding your particular type. And of course, as with everything else, you should be able to do this with no type annotation. You can write this out and just say like result equals this line of code and that's it. You just specify the format and it will figure it out and it will do the validation like right up front, like right at the decoding time and give you back an error if it fails to decode into this structure based on how you have used this structure and the rest of your format program and the type inference can figure that out. This is a work in progress. We don't have decoding ready yet. If it were, believe me, I would have demoed it but it's not ready yet. Ayaz has been working on that and I'm really excited to actually be able to like demo all this stuff in addition to encoding, which already works. Huge thank you. There are so many contributors who I would love to thank because there's just been an amazing like swarm of contributions to rock. I just decided to cut it off at the top five whose work you just saw demoed. Ayaz did all the automatic encoding stuff like the deriving and also like the general like abilities feature. He's also done a bunch of stuff on the compiler. Fulcurt did all of the rock test stuff that you just saw like last time I demoed rock we didn't have any of that testing infrastructure in place. You could not run rock test. Now we have it. Thanks so much to Fulcurt. Brian Carroll did the HTTP implementation for the CLI platform. Anton is the reason that you can now run nightly binaries of rock instead of having to build it from source and like install LLVM and all that stuff. So if you ever download a nightly binary and run it to try out the programming language you have Anton to thank for that. And finally, Martin whose work you just saw with the CSV parser. Also Martin recently implemented our new dictionary and set types in the standard library. Also I'm now excited to be able to thank our sponsors. So we actually have a couple of different companies that are financing rock. No red ink where I work. We're hiring by the way. So if you wanna come do functional programming we use like Elvin Haskell at work and hopefully in the future we'll use rock as well once it's ready. No red ink is paying me full time to work on rock. RWX is paying Ayaz full time to work on rock. Huge shout out to them. RWX really, really appreciate that like investment these companies are making and like open source software. Not a lot of companies will do that. And it's really awesome that like we have multiple companies doing this. And the third company, this is a Dutch company. I'm an American. I'm gonna try and pronounce it correctly because my instinct would be to say Tweed golf which I know is wrong. I believe it's Tweed de Hof but Dutch people I apologize if I butchered that. But basically they're paying a full court to work on rock one day a week. Very, very appreciative to all of the sponsors for funding this. And if you know anybody, any companies that want to to fund more work on rock, please get naturally because I would love to have more logos on this list. Okay, thank you so much for listening and I would love to take some questions. So is that encode syntax where you use utfa.json? Is that entirely built into the language? Or is that portion of it that utfa.json could you is there the possibility of user implementation? Oh yeah, so JSON is not built into the language. That's all user implementation. What is implemented, it baked into the standard library is the concept of encoding. So basically like the language knows how to say I can provide a, and this is exactly what Siri does in Rust. So this is like, we just kind of followed their design lead there is basically it says like, I know how to specify like a way to like encode in a generic way given a format. I know how to say like, for example, in a string you might say, okay, I am now like, when I get to this part of the record, I'm going to call this formatters, give me a string thing passing my actual string value from that field. That's what's baked into the language is the like, hey, formatter, you know how to encode strings, right? Well, at this point, I've got a string and here's like the record field name and the string that went inside that. But it's entirely in user space, what the actual format is the like, I want to take, you know, specifically or I want to go specifically to JSON or decode specifically from JSON or CSV or whatever else, all of those formats are in user space. Cool. I think Danny was next. Maybe, maybe not. I see a raised hand from Danny O'Brien, but oh, I see, wait, I also see a little icon that suggests maybe we can't hear your sound. So I don't know, I guess we'll move on. Brandon, I think was next. Yeah, okay. So you're talking about, you can, the program's still runnable, even if it's not complete or has errors. I'm trying to think about like, is that a good thing? Cause I've had like, you know, Python programs before where it's just like, oh, I have some variable that's the wrong thing or whatever and some branch that I forgot to test and I don't find out about it till later. So like, what drew you to this kind of thing for the design? Oh, so you'll definitely know about it. So like I said and demoed, rock check will tell you about it, right? Like if there are any type errors. Oh, that's right. Get a layer out, right? So certainly CI will fail, but also like if you can only have like, you know, an actual editor, then like, you know, you'll get the red squiggle right away in your editor. So like, I expect that like, you'll always know about it. It's just a question of like, basically now you have the option to say like, I want to ignore the red squiggle. I want to run anyway. But yeah, you should always know about it for sure. Shouldn't be a surprise. Okay, you're not just reliant on having to have 100% test code coverage or something. Okay, cool. It's literally, I mean, 100% like you, the type checker always knows. There's no way to like, tell the type checker to like, oh, just ignore that. It's just like, you're just allowed to run the program. Even though it knows about the type mismatches. Okay, I posted in chat what his question was. Audio is still broken, no worries. My question was in the type definitions effects section, there were asterisks in the type. What does that signify? So an asterisk in rock is an unbound type variable. I didn't really want to get into like all of that. Cause like I said, it's not supposed to be a language tutorial, but basically like that just means that like the effects, and also you saw this in the error types that they can like accumulate. If you don't have like an asterisk or a name type variable, then that means that they're sort of closed. So actually in the type of main, you may have noticed there weren't any asterisks, which is because like at that point it's like, okay, we're done. Like there's no more accumulation of these things. They don't like compose together anymore. This is just like, these are the final types and we're absolutely done. But yeah, when you're like making functions that are like being called and like calling each other and like building things up, you do want those things to accumulate. So that's where the type variables come in. Okay. I think Chris was next. Yeah. Hey, really looks cool. I'm really excited about it. Play around with Elm a little bit. So there was one thing that looked weird to me and maybe you can kind of explain. So when I was looking at the asynchronous, like the task code, it wasn't clear to me how the function would know that it was a task because you'd have this sort of like, if you have a task inside the code, you hit like task.await and then you have some pure code after that, for example. Is there like, is it, is the, and usually in sort of asynchronous code, I see like either some type annotation like in C sharp, where you have like a sync code or it returns a task and that's how it knows it's a task. Other things like F sharp, I mean which language I'm more familiar with, they have sort of task expressions, so everything's sort of inside of a task and like Elm it's wrapped, right? It is sort of like a task return type. So how does it know that it's a task that that was really kind of interesting? Yeah, sure. So I can, this is like in a different tutorial, which you can find at rockling.org, but I can kind of like briefly explain this. So let me, which one of these is simplest? I guess, yeah, let's do write out but I think that's why the easiest one to explain this with. Okay, so let's, so I got my list of movies in here and let's say that I was just like, okay, I'm not gonna do any of this stuff, just comment that out for now. I'm just gonna return, I don't know, let's some movies again, sure. So I would just be like, okay, I take in my movies and I return movies. All right, that's pretty boring. Now let's say I instead want to, actually you know what? I can actually, I can just simplify this and say what if I, actually you know what? This is such an easy example. Let's just look at the actual implementation of write output because that's all this is. So write output takes the list of movies and returns a task. Right here, I have these two constants that I'm defining here, like path equals and record equals. And then at the end, this is what the function returns is it calls file.write. File.write returns a task. So there's actually nothing like unusual going on here. It's just like literally like, hey, like this function returns a task. So that's what this function returns because the last line of this is like calling a function returns a task. So I return a task. So that's just like basic, you know, bread and butter, like that's how it works. So there's no wrapping or anything. It's just that like, you know, this function returns a task. So that's what this one does. What's more interesting is when I'm actually chain these together with a weight. So this gets into, there's a subtle difference here, which is that these are actually using a, like backwards facing arrows rather than equalities. And you notice that the ones that have tasks out of weight at the end also have this arrow. Again, without getting into like what this syntax means, this is called back passing. But basically, I guess I can just de-sugar this real quick. So back passing is syntax sugar for a way to do kind of like callbacks. So it's called back passing because basically this is as if you had written task.await passing this. And then you wrote a lambda here. This is the syntax for lambdas with the arrow facing the other way and then all of this is invented. And then we get down here and I'll de-sugar this one too. This is a task.await, get movies, well, and then write output movies. And this is the final like return value of the whole thing. So basically task.await also returns a task. So we are chaining these things together. But so that's why, I mean, I called this thing task but if I were to annotate it, it would literally say task is a task of yada yada. Innovation of Rocco. I mean, that's really cool. I like that approach. Yeah, so this is, we're dealing language that I know of that has back passing when I posted a demo of that somebody pointed out that this is similar to, there's another program in language called LiveScript which has a very similar syntactic transformation. I figure what it's called in LiveScript but that was where the name back passing came from was like, we're trying to figure out a name for it. And then I actually think Djelko who I think is at this meetup may have been the one to point out. Yeah, and so basically we sort of independently came up with that and then use the name like, oh yeah, I think they call it back calls in LiveScript. Yeah, so it's kind of similar to Haskell's do notation but it's just syntax trigger. It's not actually using any types or anything like that. So anyway, so that's what that's doing and basically the result of all that is that like I am just building up this task and then finally I call task.attempt which once again is a function that returns a task. So there's no special syntax for running tasks. It really is just like, hey, you know, this function returns a task, that function returns a task. So that's why we don't have like an async or a way keyword. It's all just based on plain old function calls and values. Does that answer the question? Yeah, I didn't understand the back passing it but I think that's really smart. I mean, I think someone mentioned in chat that it's like do notation but I mean. It's kind of like do notation without the do but also where a bind is like explicit instead of implicit and return. But I just think it's, I thought it was neat. So I didn't realize that's how it worked but that's cool. So if you had something like the idea of like in JavaScript promises, your back passing mechanism would flip that whole thing you wouldn't have to chain them. Not only are you right but actually like, I mean task is basically like promises except that they don't run right away. You chain them together the same way. We just use the word await instead of the word chain to name the function that chains them together. So really like, we could have just called a chain and it would do exactly the same thing. We just happened to name it await but that's actually also not part of the standard library. That's part of the platform but again, Canon worms I don't really want to get to here. Yeah, you can, if you want to, you could make a version of all this that has tasks with different names. You could call it promise if you wanted to although that might be a little confusing since it's not side effecting and you could give it one, two or three type variables instead of like this version has three with the effect type. You don't have to do the effect type. In fact, the first iterations that this didn't have an effect type, just kind of realized at some point that we could do that and then thought we tried out and actually ended up really liking it. So yeah, lots of different ways that you could design this. Tim has a hand up. Yeah, curious about the overall state of rock in terms of like, are you aiming for a certain niche of program spurs like small data transformation scripts or web servers or? Yeah, so we're starting with the command line apps because they're just like easier than web servers and so like building that out like the background stuff that's like in progress. So hopefully I'll, you know, pretty soon here like have something that I can like announce as like, hey, this is like, try it out. You can like really run like full fledged command line apps. Definitely don't have that yet. Like there's enough for me to like demo this stuff but like, you know, there's not enough there that you could like really build a full fledged, I think like arbitrary command line app without like running it to something like, wait, where's this? Where's that? Yeah, it's like, oh, it's not implemented yet. But yeah, servers is like kind of the next goal after that, that's what like no renaik and RWX like definitely, you know, want to use it for. So that's what we're kind of like aiming for after command line apps. But I am planning on at work, like we have a little project plan to translate like a little internal use only script but port it from Haskell to rock and try it out that way. But that hasn't happened yet because we're not far enough along yet, but we're getting there. Other questions? I am curious. I've never seen a, I mean, I've seen compilers that are, you know, able to, you know, just kind of gloss over error sections and just to generate error output, but it's really neat. I've never seen one that could actually compile and run a program that, so what was involved in that? I mean, is it something unique to rock or is that something that you've run across in another language? I mean, there's definitely precedent because Haskell does have the dash dash defer dash type errors flag, which was kind of like what gave me the idea for it originally. But as far as I know, we're the first language that doesn't have like a dynamic typing option. It's not gradually typed, whereas designed to like always be able to run. I don't want to claim that we have like done it in the general case yet, because like I said, you know, there's some cases where it works and I'm able to demo it, but like, as far as like what's involved in it, conceptually it's very simple. Conceptually it's just, okay, we hit a type annotation on this expression. That means we can't actually run this expression. So what we do is we replace that type with like a special type called like erroneous, which basically says like, hey, for the rest of type checking, just ignore this thing and like assume it's correct, even though we know it's not. And then we get to code generation, like actually generating the binary. When we hit that thing, we'll just generate a runtime exception, you know, so that if you ever get there, it'll emit a crash. However, in practice, it turns out that because this is a compiled language, in a lot of cases, like the code for some parts depends on like knowing the actual like real type of other parts. And if you don't have that right, or you're not like quite aware of it, right? Or like the implications of one type being this type affects something else, then it's pretty easy to like accidentally, you know, get in a state that like the compiler would assume is not possible to get into, because like if everything is like actually correctly typed, it shouldn't come up. So there's some like things that we kind of like assume at our sort of naive implementation of like cogeneration that like ought to be true that sometimes aren't. And that's where like some of the problems come in and we need to like basically like add special cases in there. It is entirely possible that once we like get all of this working and it no longer like ever crashes the compiler and like always successfully generates the code like we want it to, that we will find that there are some cases where it's still not as ergonomic as something like JavaScript might be. So as an example of this, one thing that comes up for us that would not come up for like a JavaScript or TypeScript is let's say I have a conditional, I say like X equals if whatever else, something else. In Rock, you know, as with Elm and like Haskell and like other languages that are like, you know pure functional type languages, both branches of the conditional have to be the same type, which makes sense because the compiler needs to know like what's the type of X? Like if I'm generating the code, I need to know like X can only have one type at runtime. Well, what happens if one of those branches has a tight mismatch? How do I decide which type to give X? Like they disagree, that's the problem, but like, I don't know. Like maybe it's the first branch, maybe it's the other branch. It's like, all I know is that they don't agree. I have to pick one, I have to sort of pick a winner and like make that be the type of X so that at runtime that, you know that branch can have its value stored in there. Maybe the ergonomics of that turn out to be bad because like it guesses wrong too often or something like that. But maybe there's a way that we can figure out like a good heuristic that like, you know, figures out, you know like unifying it with like everything else around it. It's like, okay, we can kind of safely assume that X was this thing and like this is the branch that's wrong. And maybe we can even use that to give you a better error message saying like this is probably the wrong one. Like, yes, they disagree, but like most likely it's this one's the problem because the other one agrees with the rest of the program. I just don't know that yet because we like haven't gotten that like far along yet but like optimistically, hopefully it can end up being like a good enough user experience where you just like, it always feels like you can safely run your program even if they're type errors. Is running with type error is a goal of rock or is it an experiment that you might back out of? I don't know why we would back I guess the only reason we would back out of it is like people don't like it but I just, I don't know what the downside would be. Like it's just a new option. It's something that you can do it unlocks workflows that are otherwise impossible but like, you always have the option of just like only ever choosing to run the program if it type checks, right? So there's no downside that I can really think of other than like, I mean, I guess you can make the case that like, oh no, people will like use this to like if they're close to a deadline they'll feel pressure to like ship stuff to production that like doesn't pass CI, you know because there are known type errors and like it knows that it's gonna crash. Like, I mean, honestly, like I have definitely worked at places where there's like pressure to, you know cut corners, you know, based on deadline pressure but I haven't worked anywhere that's like going as far as like, you should ship this thing that doesn't pass CI, you know like doesn't even type check because like you have the option of doing that. Like for example, I don't really hear about people doing that and like typescript plan. It's like usually, you know you're not gonna ship until your type's all checked. So I'm not really worried about that as like a downside potential. So I guess it's something we conceivably could back out of I just don't know like why we would bother now that we've like, you know already gotten like at least the basic proof and concept working. Yeah, are there thoughts, questions? So you're obviously having a lot of fun with this and learning a lot with it. So I'm wondering, you know looking at similar languages like Elm, Haskell and you know, the ML TypeScript I'm curious why you chose to start with a new language and why did you start from scratch as opposed to starting from one of these other languages? Yeah, that's a great question. There's a couple of reasons for that. So one reason is that I had a bunch of like really okay, so I guess philosophically here's kind of how I was thinking about it. How long does it take to make a new programming language? I did want to make a new program language. I didn't want to just make like Elm for servers or something like that for several reasons. One of which is that like I just had a bunch of ideas that were sort of like, you know ideas that like for one reason or other didn't make sense for Elm or like, you know just like we're not going to end up in Elm but I still was like interested in them like wanted to see how they would like pan out in some programming language. So I was like, well if I want to do those things I want to make a new language separately. You're right. It's fun. I thought it would be fun and it was actually way more fun and rewarding and has been than I even expected going into it. Honestly, it's like the most rewarding programming project I've ever worked on and I like started two companies in the past. I'm not just saying that because they failed it's rewarding for other reasons but it's just like, it's been great. So part of it was just like, yeah I almost was looking for an excuse to do it and like part of it was like wanting to do these things that just like there's no language that like wants to do these things or where it would necessarily make sense to do these things. But another part is just like wanting to explore like setting a really high bar for performance both of the compiler and of the like compiled programs like afterwards that's been a goal from like very early on in the project. Not, I can't say the very beginning but like, I mean certainly like fast compile times and like run fast has always been but like the only pure functional sorry, I don't think there are any pure functional programming languages other than rock that are like really, really performance focused other than like Milton is, I don't think pure functional it's like a functional programming language but it's got like a monomorphizing compiler which rock also does. But like, so, you know if I'm choosing Elm, Elm compiles the JavaScript if I'm choosing F sharp, it compiles to CLR and I'm choosing like pure script it compiles to JavaScript. You know, OCaml would be the only one that I know of I guess Haskell also does this that like compiles the machine code but both of them, I mean, I know OCaml is like a good reputation for performance overall but like, I know quite a few details about like how their like runtime stuff does like they have like box floats and things there's just like a lot of performance that's being left on the table there and I really wanted to get something where it's like this is not just quote unquote fast for a functional programming language one of the things that I was thinking is like I don't see any reason that functional programming languages could not be like faster than imperative languages like all of them, like we wanna be faster than go like straight up that's like gonna go from like very early on the project is like if you're writing your, you've got a go application you're like, this isn't quite fast enough you should be able to be like well, you could rewrite it in rock and then it would be faster and pure functional that's like a very concrete goal of the project I don't know how you get there from any other starting point because none of these existing starting points are as fast as go that I'm aware of maybe Milton is, but as far as I know there aren't any like functional programming languages that run that fast part of the reason for that is that I just think that like functional programming as like a paradigm is like great from an ergonomics and like maintainability perspective and I just don't see any technical reason why it shouldn't also be great from a like runtime perspective it's just that like people haven't really tried it so I wanted to try that like I said, that was not a goal from the very beginning but like from, you know, for years and years that's been a goal one of the reason though is just thinking about like all the things that I want to do with rock if I started with an existing language and was like, I wanna just like fork it and like add the stuff that I wanna add that's still like a five plus year project it's like five years of my life that when I started out I was not ever intending to do it full time I just assumed that wasn't ever gonna happen it's always gonna be like a nights and weekends thing if I'm putting that much of my free time into it it kinda sucks like the idea like to me of like putting all that time in at the end I'm like, yeah, I have this really cool fork that I spent like years of my life building like when I could have said I built this whole thing from scratch every aspect of this like was something I was in complete control of like the ceiling is just like programming like there's nothing where I'm like oh I'm constrained by like what's already here or like I don't wanna rewrite that it's like, no, we're just building it all from scratch and so sky's the limit the only limit is like really like what's possible and like without sacrificing other things like it's just the trade-offs of like the machine and the like language design that we wanna go for so basically I figured like let's say that it takes me five years to do it as like a fork, what does it take me to do it from scratch? Like six years, seven years maybe like at that kind of a time scale and also like I'm in this for the long haul I'm not just like this is not just a hobby project for me this is something like slight anecdote many years ago back in like 2011 like more than a decade ago the first talk I ever gave at a meetup I was in St. Louis this is Lambda Lounge which was hosted by Alex Miller who now does Strange Loop this is before Strange Loop I think it was before Strange Loop no, this was 2011 it might've been like the first or second Strange Loop that happened Alex hosted this meetup in St. Louis it's called Lambda Lounge I gave a talk about an idea for a design for a functional programming language and basically at the end of it at the end of the talk I was like, this is just a design it's an idea I haven't implemented this at all but like I'm just kind of curious to people think of it one of the audience members asked me a question that has stuck with me since that day and he was like, you know let's say you make this language and it's like successful and people actually use it that's gonna be your thing like you're gonna be that guy who made that language like that's just gonna be kind of your like programming life is that what you want? You know, do you want that? And I thought about that and I was like, I don't want that not for this I had fun designing this language but like I don't really want that to like be my thing I don't know what I want my thing to be but I don't think it's that so I just like never really thought about language design again until I got really into Elm and then started thinking about rock and I asked myself the same question I was like, let's say rock is successful and that becomes my thing and I'm the rock guy people have come to me at conferences and been like, hey, aren't you the Elm guy? And I'm like, I'm an Elm guy I didn't make Elm I know the Elm guy his name's Evan he's a very nice guy but I'm not the Elm guy but like, did I want to be the rock guy? And I was like, you know what? Yeah, I'm up for that like if it's successful, like I'm in and so I'm anticipating that like it's not just going to be five years or seven years if it's successful it's going to be multiple decades of my life I'm going to be like that's going to be the main focus of my like, you know career quite possibly now that I'm doing it full time so the delta between starting with an existing language and doing it all from scratch in the context of decades of my life like rounds down to zero it's like almost not more work like in the grand scheme of things to do it from scratch but it has so many more benefits in terms of like how ambitious we can be about the project so looking at it through that lens and like I said, when I started I asked myself this question I was like do I want to spend multiple decades of my life on this language? Like, do I like the idea that much? Does it sound like that enjoyable process? I was like, yeah it does I'm in for that which means, yeah why not just do it from scratch? Might as well so that was probably more longer than yesterday you were looking for but there you have it that's those are my thoughts on the subject Not at all, that's cool thank you for going into that So have you thought about what you want your title to be? I mean, there's like benevolent dictator for life but what is the rock equivalent? BDFN is the term I've been using benevolent dictator for now So the specific reason for this is that so the term BDFL was originally applied to Guido von Rossum of Haifan and he didn't end up doing it for life I mean, he retired and said I'm hanging out my BDFL hat while I'm still alive We don't actually know what happens when you have a BDFL who eventually stops living like the L clause comes up because I don't think we're ever going to be immortal like it was certainly none of us in our lifetimes which means that at some point like what happens to the language like you had this like BDFL and then their L ended and so like now what? I care enough about rock I don't want to just like leave everybody hanging just be like well I'm going to be dead so good luck with that you know Rather, I actually have already been thinking about like what do we want to transition to like at some point I'm not going to be in charge of this project what should be in charge instead such that like the project keeps going in a good direction and I actually don't think that's there's an obvious good answer to that the default answer seems to be at some point every language goes from every language that starts off with one person running it like Haskell was like committee from day one but like Russ is a good example like Graden who created it but pretty pretty soon after like actually even before 1.0 if I remember right he stepped away and he was like hey you know I did my thing like have fun with it you know y'all are in charge I don't even know if he ever saw himself as like benevolent dictator if he may have you know from the get go saw it as more of a like consensus driven thing but the default seems to be after there's one person in charge there's a committee in charge and I don't know of any counter examples other than maybe Python and Guido has only been out for like you know a couple years now so it's a little early to tell since Python's like a 30 year old language but with the possible exception of Python maybe setting a new precedent here all of the other committee driven languages if you graph language complexity over time it just goes up and up and up and up and up forever until eventually the language is bloated no matter how simple it started out every language that I've ever seen that has a committee designing it over time just becomes a bloated language period again hopefully Python after like you know whatever 30 years of Guido and then like two years of not Guido so far it doesn't seem like it's gotten really bloated but I don't know we'll see in another decade or two maybe it will but I really don't want to see that happen to rock and so I'm not sure how to transition from a like whereas you look at like Elm and like closure like and Python sure like these are BDFL languages where the person running them really cares about simplicity and so they're able to just say no a lot you know they get a lot of feature requests and they're like we're going to be really really careful about accepting these so that you know maybe the language doesn't get more complex over time but the slope is like very different you know depending on you have one person saying no a lot or like in the case of Elm you can even like add some stuff and take other stuff away such that maybe the language gets net simpler over time which is even better if you can get away with it if you can find a way to you know like organize the piece as such that it works that way although of course after a language gets to a certain point like backwards compatibility becomes more and more valuable so that's a trade off there too but so I consider this kind of an unsolved problem I don't know like how to transition to a model where like you don't have this incentive of like and this is the problem with like there's definitely an incentive problem with committees where it's like let me pitch you on this like you're going to join this language committee and really what I really ideally what I'd like you to do is just say no to everything like get on the committee and then like you just don't do anything other than shoot people down all the time does that sound like a fun time you want to sign up for that like okay pitch number two hey sign up for this language committee you're going to get to add cool new stuff to the language like that sounds way more fun and so like committees unfortunately just have this baked in incentive to like add stuff because like they select for people who like want to go add stuff so how do you instead select for a committee where it's like incentivized to like try to be more of a like conservatorship where it's like we want to protect what's here and like probably the number of features we should add over time is not zero but it should be really small and we should be really cautious about it and say no a lot how do you select for that in like a committee I don't know I actually do not know the answer to that question but like my goal is to try and figure that out and like live long enough to see it actually get implemented so like I get to like walk away and be like cool like you know we've transitioned to this model that like hopefully will outlast me and like and work long-term and I got to be around to see it I don't have a specific like date in mind for like when to do that but it is like part of the plan that like it's BDFN not BDFL I definitely want to like at some point consciously transition to that you know when it feels like we've got a good way to do it I have no idea what that will be though Build a committee that's entire job is to put together an expert system that just says no in new and creative ways I mean honestly it would be easier if it was just like so I do know of one it's not a programming language but Donald Knuth his what is it I think it's LaTeX and it's one of the ones I'm totally blanking on the name but the version numbers are digits of pi so it's like it's currently on version like 3.1415 you know something something and the rule is it's supposed to keep adding digits of pi on every release until the author's death at which point it is declared finished and all remaining bugs are considered features officially and it can never change after that can never have another release I definitely don't want to do that with rock but you know there is something to be said for like if you have the heuristic of all new feature requests are automatically rejected forever that does prevent it from getting more complex but unfortunately like new hardware comes out like computing changes out from under you like new requirements emerge like in the world that like didn't exist before because the circumstances change and there's no way to anticipate all of those so I think if you refuse to add any features forever as like a permanent policy eventually someone's just going to fork it and like they're just going to like go do their own thing so I think you have to like have some sort of plan for like being able to add not zero features but like the minimum number that preserves what's nice about the language while still keeping it as simple as possible and like adapting to changing the circumstances no idea how to do that yet hopefully we'll figure it out I do have one minor criticism you've left you've left left a joke on the table it's the fact that it's rock run and not rock roll so many jokes that a fun name for a like a rock based tool would be role spelled ROL like a rock ROC is actually like it's the name of mythical bird that's why the the logo is bird but yeah I mean but it's also like you know ROCK without the K so like yeah what if you just had like other other words like that where you leave off the last letter it could be fun um yeah any other uh thoughts or questions or anything if you have one in the chat it's like oh both task and task dot task type signatures are they different things no that's actually I noticed that as I was demoing it one like that's just really qualified the task type comes from the task module there was one place where I accidentally fully qualified it like even after like importing it unqualified so I could have deleted that task dot task and made that task and so would have compiled I actually was like well I noticed this but maybe nobody else noticed it so I'm not going to draw attention to it the demo but of course you noticed it so well done but uh yeah it wasn't necessary I could have just deleted it everything was still worked uh yeah any other questions thoughts can you hear me oh fantastic I was wondering if you could say a word about I was I was just before this was watching your edge discussion and it seemed to me that that would be an excellent model to follow addressing your your complexity problem because if you if you were able to manage that that I'm some platform layer as as like okay people want to add stuff they add it as their own platform right that seemed like if you could say a word about that I think I I I found it that very fascinating yeah so in a lot of cases that would work but in some cases I think there's just no substitute for new language primitives so classic example this is SIMD this is something that like 20 years ago was not a thing in hardware at all SIMD is a single instruction multiple dispatch maybe direction I don't know but anyway it's a big deal for performance a multiple data thank you yeah so basically it's like you can have a cpu instruction that will do like for example like if you've got four numbers in a record it can add all four of them at once give you back a new record that's got four answers in one single cpu instruction so if you've got data that happened to be organized that way which comes up a lot like in vectors for example you can do stuff that's like literally four times as fast or almost four times as fast it's really really powerful so platform stuff in rock because platforms can do arbitrary side effects it all has to be like behind the task equivalent that's not a restriction you want to have on something like SIMD that's really something you want to have as a language primitive because it's like it's math it's like arithmetic but again that that only exists now because hardware changed so it's both something that I don't think would be a good fit for platforms but also something where there was no way to anticipate that coming out and like I think there's going to be another thing like that at some point in the future where there's going to be a new thing you do need a new API to deal with it like some compilers can like auto vectorize which like takes some advantage of SIMD but it's like it's kind of only the tip of the iceberg of like what the performance gains you can get if you actually have like language level support for SIMD this is something we have planned for rock but haven't yet is SIMD support but it's basically like you know there's really no way around as I see it adding language support for something like that but there's also no way to predict when you're going to need it so that's the type of thing I have in mind when I'm thinking about like you know the number of features to add is probably not zero but it should be low it's things like SIMD like yeah new things coming out of the environment other questions do you have a roadmap on when you want to release rock to the public no roadmap like basically like have some like goals and stuff but like one of the things like before before we do like a certainly like a 1.0 release like way before this I want to do a lot of stuff like the editor I've talked about the editor like other places like rock has a built-in editor it's like very very early stages but it's a really important part of the project and so like certainly like we're not anywhere near a 1.0 release at some point we do want to have like an ODOT1 release but there's like some other various targets that we want to hit before we even do that but yeah I mean to me like an important part of like releases is communicating some degree of like stability and maturity like these days I saw a comment on Hacker News that was like I forget what it was some like programming thing or just something like that that was like on release ODOT3 and someone was complaining about you know I really expect more from an ODOT3 release of a of a software project this day and age which kind of drives home it's just like you know ODOT1 is like almost the new 1.0 like you know in the old days you're like whatever your first ship that was 1.0 right but now it's like people ship so much sooner than that and like start naming versions so much sooner than that but like people have developed expectations reasonable or not that these are like expectations that some people have about like levels of maturity and so at this point like part of the reason we don't have a number to release yet is just like it's completely immature like you know all the stuff that I demoed like I know that there's like a bunch of stuff that like doesn't work that I of course didn't demo because I know it doesn't work there's compiler crashes there's you know unimplemented features there's just just a lot more work to do and so you know at some point we'll get there but like I I've been really really consciously trying not to make commitments about like timelines around that just because like I want to give us like the flexibility to to like you know do things well basically and not feel a lot of like time pressure to like stick to you know commitments that we made and I've been very fortunate that all of our sponsors are on board with that philosophy like sort of slow and steady I really appreciate like no red ink and rwbx and the Dutch one that whose name I can't quite pronounce Trey de Hof I think is yeah like all have been have been really cool about that yeah so I guess that's my follow-up question and is like how did that happen what is no red ink does no reading have like a planned application for this kind of thing I mean so so we want to use it on the back end basically like for servers so the way that we use Haskell at no red ink is pretty unusual I mean basically like we really like Elm that's that's kind of our deal like we're as far as I know we still have the world's biggest Elm code base it's like approaching a half million lines of Elm in production and we've had it since 2015 that was like when we first introduced it to the code base and we've been trying to get an Elm like experience on the back end for a long time and the closest thing we found is basically use Haskell but like kind of pretend it's Elm like to the point where we actually did stuff like we ported a lot of Elms standard library to Haskell we ported the Elm test test runner to Haskell we haven't quite gotten to the point of like porting Elm format to Haskell but like we've basically already been kind of like reinventing a lot of Elm in Haskell but it's still not the same because you don't like the Elm like error messages in Haskell like Haskell error messages are like kind of notoriously not great you don't get the compile times of Elm like GHC is a lot slower than Elm so there's just like a lot of cases where like we just like couldn't really get that experience that we were looking for even though of course we want like a pure functional back end you know that's that's like what we're in for so with Rock what like sort of the pitch is that um you know once it's mature that we'll get something that like actually gets all the things that we like about Elm and like you know hopefully even some like additional things like I'm not trying to just like recreate Elm of course I'm like also trying to like try some things out that I hope will be even better these are experiments which don't necessarily make sense for Elm because Elm is quite a mature language at this point but um but like you know these are things we can try out and if they don't work out we can always like fall back on like just doing it exactly the way Elm did it because Elm's great language so the pitch is like is getting a lot of benefits like faster compile times which is a productivity boost better ergonomics like less time staring at inscrutable error messages and stuff like that you know happier team like getting just like nicer user experience and on top of all those things I think there's going to be some like very serious productivity benefits unlocked with the editor and I've like talked to my boss about how like that's going to be years of years from now but like you know the benefits could be like like really serious but also just like honestly we've had a lot of success as like from a like hiring perspective of just being like a being known as like a functional programming shop and one of the things that was like really effective about and surprisingly effective about adopting Elm early on was that there was this community of people who were like really excited to use Elm but there were very few companies using it and on the back end we've seen that same kind of thing with Haskell but because we're using Haskell in kind of a weird way where it's like this Elm flavor of Haskell it's not really like Haskell flavored Haskell we haven't gotten that same kind of like pool of applicants because in a lot of cases like people who want to use Haskell at work want to use Haskell in like a very I don't know Haskell-y way they don't want to use it as like Elm flavored Haskell so kind of part of our hope here is that like another benefit of Rock will be that we can attract people who like actually I just really like the ergonomics of like an Elm-like or like you know Rock-like language which are very similar and and that you know we can attract people who you know want to do that so there's a bunch of different reasons for wanting to do it but really to kind of boil down to just the fact that like there really is no language that feels like you know Elm-like user experience but for like servers and and Rock is kind of like the most promising way to do that so that's that's what other companies are excited about as well. So far excited enough to take a bet on it before it's even ready which I again really appreciate the the vote of confidence on that. Yeah that's really cool thanks. Right about the two hour mark any other questions I think the recording is going to stop at two hours if I remember right from the email. That's what we said I do have a button now that I can actually stop the recording whenever you're ready. Cool I mean if there's another question you know we got a couple minutes so that's fine. Thanks cool I guess uh oh was there another question? No I was saying no there's no other questions or I don't know. Cool good timing then I guess we wrapped it up right in time. Very nice well thanks everybody I appreciate all the questions and all the interest and yeah thanks for having me. Thank you.