 Okay, hi. I'm John. Thanks, Mark. I'm going to kind of give a low-level introduction to DSLs in Kotlin for the uninitiated here. This presentation is an adaptation of my Kotlin Thursdays article that was posted a couple of months ago. So if you want more detail, definitely go there and check that out. Let's just get into it. What, uh-oh. The presentation didn't quite do what it's supposed to do, did it? Just do that? Okay. All right, first let's talk about what is a DSL. Now this definition is by no means academic or official or from the dictionary or anything, but this is just kind of my understanding of what a DSL is in the context of Kotlin. So it stands for domain-specific language, and your domain is basically consisting of structured data as well as interfaces, and to get to a DSL, you're going to start with basically functional code, right, so you have your data and then you have your logic. And you want to extract all your common code and higher order functions into what I'm going to call the interpreter. And then everything that's left, which is basically all of your data that's going to be input, as well as any of your implementation-specific code, for example, like any lambdas that you might want to pass in as custom functionality for specific implementation, that becomes your domain. And if you think about functional programming, you know that code is data and data is code, right? So ultimately what a DSL is is a way to represent the data that is the input. And you're crafting a constrained language around that, so your interpreter knows how to interpret the input data, the structured data, and it will run any of your lambdas as part of like a well-defined behavior, like an interface. So this might be kind of like very theoretical at this point, but I'm going to show how this all plays out in Kotlin. So Kotlin lets you build your DSL in a type safe way, and it has some specific language features to help you do it. So to simplify things, you could think of a DSL as basically a fancy builder pattern. And the reason why it's different than say, for example, just plain data like XML or JSON input is because we can actually express higher order functions in lambdas in Kotlin and store our information as basically data, but it's Kotlin code that is providing the data and as well as those functions. So let's get into it. Let's get into the Kotlin features that let us write DSLs. How do I zoom out on, yeah, I think it's a little, the resolution on this is a little, sorry, what was that? Okay. First, we have infix functions. So infix functions in Kotlin are for simple binary operations, and it's really just there as syntactic sugar to eliminate the dot and the parentheses from that binary operation. So for an example, I have here basically the prime example of an infix function in Kotlin, which is pair. This is a class that comes in the Kotlin standard library. And the Kotlin standard library also provides an infix function to help you build a pair. So instead of calling the pair constructor and passing in the first and the second into it. Instead, you can use this to infix function. And so I have a sample declaration for the infix function here. And it actually, there's a typo here, it is missing, oh no, it is missing the to, sorry about this, but the to should go right where my cursor is there. So we can just create a pair by calling some instance of string space to space string and it will call the constructor for us. And that helps us just make our DSL have a more natural language feel to it. We don't have all these parentheses everywhere, which is not a part of real language, right? And it's just a syntactic sugar thing, but it can help us make our DSL more expressive. Next we have lambda as a final parameter. This is another feature of Kotlin that allows you to lift a final lambda parameter out of your function called parentheses. So if the last parameter of your function is a lambda, just any lambda, you can just kind of move that lambda outside of the parentheses when you're calling that function. And it's a weird trick, it confuses a lot of people when they first see it because like why? What's the point of this? Why can't it just be like Java? But what it allows you to do is build a construct like you see here on the right at the bottom where f, the call to f now doesn't look like a function call, it actually looks like a language block. And it makes it look like f is a keyword in our language. So that's part of defining the language. Really it's just a function call. But the point of the DSL is to make it look like you have a constrained language here. Next we have lambdas with receivers. These are something also unique to Kotlin and they allow you to provide a lambda that's going to be invoked on a receiving object. What that means is it's kind of like the apply top level function in Kotlin. You're going to provide a lambda and within the lambda scope you're going to have this and this will be a thing of the type of the receiver. So the example here shows basically if we wanted to wrap a string builder with this type of DSL function. We could write a function in this case called string DSL that takes a receiver. And it will build a string builder and apply our lambda to it and then call build. So within the call to this function you see at the end we can just make calls that we would on an actual string builder but as part of a lambda. Okay, next we have the DSL marker. This is the last language feature that we're going to cover. This helps us restrict scope. So this one is a little bit tricky but if you look at the bottom of my code example here you'll see like an example usage of this DSL of the code that's listed above. Now you see it's annotated with my DSL marker class. So it's basically an annotation. You create an annotation and mark that annotation as a DSL marker annotation. That's what you see on the left. You apply that to any class or function in your DSL and it will restrict the scope within any lambda calls to that particular object. So you'll see in the C2 block in the example I can access prop 2 directly because I'm in a lambda inside the C2 object, the class 2 object. But if I want to go up the hierarchy into the C1 object and access prop 1 which only exists at class 1, this I have to, I can't do. If I didn't have that kind of this at CL, C1 syntax there, it wouldn't work. Without the DSL marker it would work. Put in the DSL marker it will not work and in order, but you can escape that scope by using the scope syntax that I have here. So this helps, like when you're doing a DSL you end up with a lot of nested blocks usually. And so when you do a control space or whatever in your IDE, whatever your IDE autocomplete hotkey is, you don't want to see every single thing that is in scope for all the levels of your hierarchy all at once because you're going to get confused. This lets you limit it down to say at this level you can only call and have access to the things that are actually on this leaf node of my nested calls. Okay. I expect there will be questions. So let's go to questions about these language features and then I'll move on with my examples. Okay. That's C1. Yes. Yeah, it does, maybe it's not the best font. Yeah, so what it's referring to is, this is a scope clause and allows you to specify what scope this is because at this point in the code there are many things that are actually this because we're in these nested blocks. And so normally this just kind of like in Java or like for example in Java, this just gets erased. Like you just don't have access to the higher level this is. You only have access to the one that's lowest, that's closest to you. Kotlin does allow you to break out of that using this syntax to say this at and then specify the thing that, like the this that you want. DSL marker is a Kotlin thing. Correct. The talking cube. Okay. Yes, that's correct. So, yeah, the DSL marker language feature is what limits the scope. Normally in Kotlin you would have access to that top level this, but like you wouldn't be able to call this, but you would have access to anything that is on this. So if a say for example a function name conflicted, like say C1 and C2 both have something called prop one, you would only, you would get the one that's closest to you. And even without DSL marker, you can use the this at C1 syntax to break out of that and specify that that's the one I want, the one up there. But with the DSL marker here, even if there isn't a name collision, you just won't have access to anything that is in the higher levels unless you specify the this at. It's really a usability thing. It's just when someone's writing your DSL, like the whole point of Kotlin, one of the points of Kotlin is that it's type safe, right? So you want like that code completion from your IDE and you want to see what are the things available to you right now. And with a DSL usually the only things that are really relevant to you are the things that are in scope of the particular block you're in right now. If you can just call whatever you want at whatever level, maybe you're using the language not as it was designed. So it's a way to just kind of make it easier to know what is this stuff I'm supposed to be doing here at this level. Any other questions? So let's look at an example. I'm going to talk about static web development for my example. So what is static web development? It's basically, well, everyone's familiar with web 2.0, I hope. It is the kind of paradigm shift that happened in web development in the 2000s that said, well, instead of just plain old HTML, we can build whole applications using JavaScript and XHR and all this cool stuff. Okay, but ultimately that is a hack. Let's be real about it. Web technologies were never built for building applications. They were built for delivering content, documents. HTML is a document format. So we see a lot of abuse in the real world of the web technologies that we have. And for example, your regular news site, say CNN.com or something like that, is loading megabytes and megabytes of frameworks and JavaScript and all that stuff to deliver just a news article. But it's still possible to use plain old HTML, and HTML5 is great. It's got some great features. So there are some advantages that we can leverage if we go back to just using HTML. We have security. There's no executing JavaScript that we have to worry about vulnerabilities with. We get reliability. HTML always works. Speed and scalability. If it's just static HTML, that means you can cache it. It's a small download size. You don't have the infamous node modules directory, which is about 10 terabytes. And your web page is fast. It's portable. It can be served by any host. It's accessible. Screen readers do a great job with just plain old HTML, no JavaScript. And it's simple to maintain. There's no executable code here. You don't have to worry about that. Of course, there's disadvantages. So if anyone here has hand-coded HTML and CSS, you know that that is a pain. And you're going to end up duplicating yourself a lot. Because on your website, maybe you have a title bar that's on every page. You don't want to hand-code that every time. I'm getting there. OK. Also, there's things that you want to do on the web that aren't just websites. Like, for example, RSS, like a feed for your site. Hand-coding the XML for an RSS. If you thought HTML and CSS were bad, try doing that. It's not going to be fun. So we have some solutions here. This is what Jonathan was referring to. There are static site generators, such as Jekyll and Hugo. Jekyll is what GitHub pages uses, if you use that. And these are basically frameworks that generate static sites for you, but give you reusable elements, additional features, such as RSS feeds and things like that. But you are still limited by whatever those frameworks provide. So it's great. Those are great tools for someone who is not super technical and wants to just build a web page. But I'm a developer, so I want to write code. So I wanted to have a little bit more fun with it. So Kotlin, in fact, JetBrains has an HTML DSL as a reference implementation DSL for Kotlin. So this is open source built by JetBrains, and it is a type-safe DSL for building HTML. Disclaimer, it's not 100% perfect. It's missing some things. For example, one thing I noticed is that the rel property for links is just not there. OK, so there's flaws. But it's mostly an implementation of the HTML spec in a type-safe way. So for example, on the left, you can see our usage of the HTML DSL. And in this usage, you can see a lot of the language features I talked about. We have the lambdas with receivers. We have the lambdas lifted out of the parentheses and all that stuff. So with the code snippet on the left, I can generate the HTML which renders to what you see on the right. In addition to that, JetBrains has a CSS DSL. So we can style our HTML using a type-safe DSL. So here's an example of just a regular P element with some inline styles. Of course, you could also build a separate style sheet file using the DSL. It doesn't have to be inline. But that's just this example. So the code on the left, which is using a combination of the HTML and the CSS DSL, will render the web page content you see on the right. So here's the point, though. We can do that, but how is that better than just writing HTML? You have an XML schema that defines what HTML is. You could have code completion and all that. Well, here's where we get into code reuse. We want templating, basically. We want to be able to reuse elements of our site in multiple places. So here's an example of extending the DSL by providing our own function on the body element that will generate our navigation for our website. So on the left, this is a generic function that can be used for any navigation bar. It's a reusable element that I could build that I could use on any of my sites to build a navigation bar. And it's just doing some very simple, like applying a class, doing some inline styles. But what it does is provides an unordered list that we have then a lambda with receiver on. So that means within the code block invocation of our function, we will get to basically build out our list. So it's a semantic list, which a nav bar, if you're doing semantic web development, basically should be. But it's going to take away all of this other stuff that we don't have to worry about every time. It's basically a higher order function. So on the right, you see a usage. So this, we're going to say, on our website, we're going to create a side nav function that's going to build our specific nav bar. And to do it, we're going to call this shareable nav bar function and then just build out our list of links that we want in our nav bar. So there's just li elements, just plain old HTML elements. I could create possibly a side nav item class to contain that information and maybe eliminate a little of this boilerplate, like not having to specify href and things like that. You could clean it up a little, but this is just a simple example. And then in my actual web page, which is obviously an HTML page, you have to have that HTML and the head and the body and all that stuff to build a proper web page. So that's what you see on the bottom, is how this could be used on an actual web page. Just call the side nav function at that point. It's in the body element. OK, so as I alluded to, we could build custom objects to represent in our DSL. And so what I showed before was an extension of an existing DSL. What I'm showing here is more like what you would need to do if you were building a DSL from scratch. You use a backing object to define your discrete objects of the domain. And there seems to be a kind of convention where objects that are backing objects for a DSL are in all capitals. And when asked about this, I think it was, I want to say, Roman Elizarov was asked, why is this? Like why in the HTML DSL and the CSS DSL, why is this here? And basically the answer was, well, that way you know you're dealing with a DSL object. But who cares? I'm not really sure. But you'll see it around. So the HTML class you'll notice is all caps. Mine entry. Class here is not required. It's just a convention. So creating a backing object means you want to annotate it with your DSL annotation. This example is building a DSL on top of an RSS library that exists on the JVM already. So we can wrap existing libraries with a DSL. I could build my own kind of like objects that represent the data that goes into an RSS feed if I want. But in this case, I'm lazy, so I'm just extending. If you look at the send entry input, it's a class from this library that I'm using. So it has all of the properties and stuff like that that are relevant to an RSS feed. I'm piggybacking on that, but I am providing some additional functions that apply some opinions that I personally have on my RSS DSL that make it easier for me to actually build out my feed by just doing a series of calls and passing in particular content that I want in my feed. So instead of having all of this boilerplate code on every single entry of my RSS feed, I really can just simplify it and just pass in the raw HTML or string that I want in my content and it will build my feed for me. So basically, a full DSL from scratch will exist of multiple of these kind of backing objects with functions that take lambdas that will like build out the tree of data that represent your DSL. Okay, so basically you can DSL pretty much anything, really then, like I said, it's just a fancy builder. So there already exists HTML DSL, CSS DSLs, DSL for Android views, Java FX via tornado FX has a great DSL. I've written my own DSLs for doing recipes, cooking recipes, slide shows. This slide show is built with the DSL that I wrote, a blog, I've written a DSL for that as well. So what ideas can you think of? Go out there and write your own DSL. That's the only way to really get a feel for how all of this stuff fits together and don't be intimidated by it. Look at some of the ones that are out there for ideas and as you start to write the code, you'll learn a lot about Kotlin as a language and you'll learn a lot about how like code can go from, like being, you can go from OOP to functional then to declarative because that's really what DSL is giving you. It's taking functional code and making it declarative. So it's like a whole another level up for you. Any questions? Yes? Sure. So let me first show you what it looks like. Let's look at my recipe for seafood fraud de volo. Okay, so this is the output, what it looks like. And let me go to my GitHub because this is not my computer so I don't have the code here. How do you, okay. So here's the code base for my website. Everything on my website is static and written with Kotlin and DSLs. So here's the implementation. So here's what the code looks like for my recipe. So this is like not, doesn't look like code really, does it? These units here that you see here for the ingredients, that's an infix function. That's what gives us that. Unfortunately, we don't have the best syntax highlighting GitHub here, but this is what lets me do this kind of syntax, is the infix. Okay, and then if we wanna see how the actual DSL is implemented. So we have backing objects, for example, a recipe, which is made up of ingredients and instructions and has these functions that take lambdas that actually do these things. Add these things to the object. And like the root of the DSL is basically, these are just the top level functions that don't exist on any particular backing object. So this is how you start a recipe. This is the entry point for the DSL. And then we have these helper infix functions that give us access to these units. Yeah, and I think, yeah, we have like an ingredient. Yeah, these are just like plain old Kotlin objects. Yeah, let's look at step. So each step, yeah. So these are like backing objects. These are just examples of backing objects. So yeah, it's not that hard to build your own DSL. You just have to have an idea of reusable patterns of code. A function lets you reuse logic. A DSL, or a higher order function, lets you reuse patterns of logic. That's basically what it is. You insert in calls to whatever lambdas you're passing in, but you have an overarching pattern that you're replicating. Any other questions? Okay, so Jonathan's up next. He's going to go deeper. I did get a sneak preview of some of the stuff he's talking about. It's a lot deeper than this. It's cool stuff he's been doing. So I'll turn it over to him now. All right, thank you.