 Okay, cool. And so, okay. So real quick before we start to talk proper, just a quick note about us. We are improving, I'm actually located pretty much within the Stone's Throw of NLV. So we do offer a free hosting for like user groups. So we have a user group that you want to set up. Just let us know. And we'll provide the space, approximate to all that good stuff. I reach out to me. We're also hiring. I know it's a flush of program groups, I don't know how many folks are gonna be on Java or .NET, but if you are, know someone who's interested in any of these roles we have, again, feel free to reach out to me. So you have those open roles. Of course, we do also certifications. So scrum, Kanban, all that good stuff. So if you're interested, you know the routine, reach out to me. And then finally, if you do get a chance, if you can scan and secure our code and just kind of let us know in terms of how we did on the hosting, that sort of thing. It also gives us a headcount. So we know how many people are managing to serve and just again, if there's anything we can do better, that lets us know. So that's about it. If you have any questions, let me know. And we'll give you a second to scan your codes. One, two, three, four, five. I think it's a, okay, I'll do it again at the end. But in the meanwhile, you go ahead and share Chris now. Let me get on. Give it to you. Cool, okay. So I'll just do a very quick introduction. Chris Bremmer, he's gonna introduce himself, but he's one of our own. And we always enjoy when he gives a talk. And it's been a while since he's done a talk in person. It's been online mostly. I don't know if I've ever given a talk here in person. Okay. No, maybe. No, I don't think I have. But, so yeah, so no, it's nice to have you. No, I have. I'm sorry. I haven't given a talk in person, but. So I want to welcome Chris and see you again. Except when I have that. So yeah, I'm Chris. I have a blog that I don't really keep up. Sealgram.me. I'll probably put notes for this talk up there. I work for a company called NOV, which is a very big manufacturing company. I work for a smaller business unit in NOV called Reed-Hycologue. We make drill bits. I'm actually in charge of creating software to help support downhill simulations of drill bits. Basically a lot of rock physics and things like that. It's pretty cool. I'm more on the programming side, but I've been doing a lot with some of the physics models as well. I have a LinkedIn site. If you guys want to check it out, I also have a QR code. Please have me try to sometimes hire functional programmers. And I'm not sure. Our group will probably grow next year. We're deciding petroleum data engineers and functional programmers, but please have me. You're interested in working with us. Happy to take a look. I'll leave it up. One, two, three, four, five. Okay. I decided you. Thanks, Noah. You got a request from me. Cool. Let me tell you a little bit about this talk. So this talk is called Let's Try Valera. I'm gonna be talking about the AdSharp web framework called Valera. I'll give you a little bit of background. I'm not sure quite who the audience for this talk is. I wrote the talk originally for a .NET users group. So if it feels like the audience is focused towards C-Sharp developers, which of course, .NET is C-Sharp. It was, but I think they were interested in it. And I think it gave people a chance to see what's interesting about AdSharp, what's interesting about functional programming and what's interesting about, well, I think they're all already sort of excited about WebAssembly. But really, so behind this talk, right, is the story of a small team, and he's familiar with the story of four people, engineers, data scientists, with a legacy app. So here it's ever done with the legacy app. I'm sure many of you have ever seen a legacy application. The worst thing about legacy applications is that their legacy, because they were written a long time ago, and they're probably making your company lots of money. So you really have to keep them up. And this one was in pretty rough shape, or still isn't pretty rough shape. Written a long time ago by a long forgotten people, bunch of consultants who think the consultancy is now defunct for reasons, I feel I understand. In an ancient and arcane script, AngularJS, I feel it's rich in need to talk about AngularJS as an ancient script, or arcane script at a functional programming workshop, but still be it. AngularJS is the original Angular, right? It's the, I would say the parents of all modern JavaScript web frameworks, right? It was the first one, and they got it completely wrong, and they completely rewrote it. And now it's Angular. It's actually not that bad, but there were clearly a lot of mistakes there. Being the first one out there, but then because it was actually a good product for the time, a lot of people wrote all of their business critical front-end apps in AngularJS. And so we suffer the punishment for this sin. So the problem with it too is it's a small team, right? It's intentionally a small team. I don't want to have like a huge team. And we have a data scientist. She works mostly in R and Python. So we want to rewrite the in stringlet. She's actually, it'd be not a terrible option, but it wasn't really going to work. And she's got other things she's working on. We have a MATLAB expert. MATLAB doesn't really do web development very well. And I've started C-Shark software engineer, who's very good, but just doesn't have like the background in front-end development. I mean, I have a pretty good amount of experience in typescripts and UI development, but I'm the manager. So this is sort of a problem. I've got other things to worry about. And fundamentally the problem is front-end development is hard. It's who I guess maybe, I don't really know how to get interaction with the, with the group, like the chats. Like I give the chat window like off of the side or even I've got an eye on it. So I mean, if anyone asks, I can also check the chat for you, like you know. Yeah. So if you want to jump in, if you want to just like say something. I am curious. 1.5 AngularJS? What per unit of AngularJS? We've upgraded to 1.14. Okay. So we were able to get two components which are better than directives. But yeah, it's, it's, it's been a long, I didn't actually, well, I'll get to that another time. But the point is front-end development is hard. So the problem is right. First of all, it's a discipline where you don't just need to understand computer science. You need to understand some design. You can't make things that are ugly for your users will hate them. Psychology, because people are interacting with your application and you don't want them to get mad. And you also have to understand asynchronous programming which wasn't always easy because everything in the front-end is asynchronous. You make a request and you have to wait a little bit and return something. So, so everything is, is sort of not but I would consider, you know, a sequence of instructions. You have to deal with callbacks and all these sorts of things. So it's complicated. I think it requires a lot of skills. But the other thing is if you think about sort of your architecture of your stack, right? Your front-end is at the very edge of the dependency graph in your stack. What this means is it's completely disposable. Oh, sorry, fully disposable. But it should be the most disposable part of your application, right? It's not a database. It's not something like where if you change the database it could corrupt your core data. It's not like, you know, an important message to you running in the background where you sort of have to deal with upgrades to it. It probably touches the rest of your stack through a REST API and you can easily update that REST API and the endpoint of the application. So I feel like it doesn't have a lot of benefit to spending. There's not a lot of benefit to getting too clever with the front-end. So it's important to keep it simple. Now, some parts of front-end development are actually pretty easy, but this is also a problem. So I don't know if anyone has experience working in the node ecosystem. It's fun. I actually really like TypeScript, but there's a problem. If you want to try out the latest, greatest front-end awesome platform framework, create a new web app. You can download a script from the internet that will run it. It will download half of NPM, which is the node package manager. And it will create something that looks pretty good, right? And the problem is, if you're like me, you'll try it out and like, wow, this is pretty productive. And then six months later, you try to run it again, all the dependencies are out of web. Here's the thing. In the node ecosystem, I have a package manager for my package manager. So how does this work? I use NPM as my package manager, but not all packages in NPM are compatible with all versions of NPM. So I have another manager called FNM, which manages my version of node and NPM that's on my machine and running at a given time. This is very frustrating. So I think it could be very productive if you're touching the code a lot. But in this case, we don't have time to, we don't have budget to. I think a good front-end UI developer does amazing things, but we just don't have someone full-time working in that. It's a monoripo. Well, it is a monoripo. I mean, it could monoripo then all the NPMs that are in this works, could it be negative, could it be negative, could it be negative, could it be forward? Yeah, yeah, you can do that, but then they come up with like, you know, they're good to be, they're good to be on there. Is this a question? Focus on the back-end of the data, just a factor of your business domain, because I can see where, like you mentioned, someone constantly touching the front-end, if your business domain was surely user experience. Yes, no, my business domain is absolutely not purely user experience. My business domain is a bunch of engineers and they get what they get. They're internal customers, like, and do their export to Excel. And the important thing is that I am not the worst UI in the company, so they hate me less than the next guy. That's a little... Look. The, they get mad, they get mad when, yeah. Okay, look, what is the node ecosystems ever evolving? It's complicated, it's something, if you're not touching it, I found it to be very difficult. So it kind of came up with some requirements, right? I did, I want to stick with the .NET ecosystem because that's sort of, at least two of us have good experience in .NET and I feel like I'd like to build that group. We want something that's easy to maintain and reason about logically for, at least for app-sharp developers. I understand that functional programmers maybe we think differently, I don't quite know. But something that works well and requires minimal effort for us to create and maintain websites. Performance, search engine optimization. We don't need an e-commerce plugin. Here's, like, you know, it doesn't have to be that mature. It just has to be something that works for us. And I think that Bolero is pretty great. So Bolero is an app-sharp framework. And the way I think about it, it builds on, I have a little trouble with my screen. Make sure that everyone can see it. There's a web-sharp framework that builds on Blazor, which is basically .NET WebAssembly compiler and Elmish, which is an implementation of the ModelView Update architecture written in app-sharp. This framework is created by Intellifactory. So it says app-sharp open-source shop that's been around for a long time in it. It's a joy to program it, I really like it. I think it's cool. I really like the ability to use MVU, kind of the Elmish architecture. In a lot of ways it's better than the original MVU architecture, which is the language of Elm. And it's extensible and I'm able to do the things that I wanna do in it. So that's kind of what I wanna talk about. So I'm gonna start off talking about what is the MVU architecture? I think a lot of people who don't think about front-end a lot or haven't looked into the Elm architecture, but be curious and why is F-sharp good for the MVU architecture? The second part of the talk, I'm gonna talk about some of the challenges that we deal with with this kind of framework and how can we make it better? How can we improve this MVU framework with some plugins that we can write? And finally, a little bit about event sourcing because it wouldn't be a programming talk in 2023 if I didn't mention event sourcing or CQRS. But really this architecture makes event sourcing super easy and you could do some fun things with it. So this is a coding talk, right? I actually have a repo, a GitHub, it's public. I'll, I think we can share the link after the end of the talk, it'll be on the website. But it's just a GitHub repo and it's not, I don't know, I don't feel like, it's just kind of showing how I interact with this website, how we make a sample application. This isn't the actual application that we created, which is in production now because it's, you know, owned by the company, whatever. So I made something that was easier and I could run outside of our firewall and on that side. So really quickly, again, I'm trying to focus on, this is the annoying thing is that sometimes a writer doesn't want to render my cool slides. So I have to have a pre-rendered. So first of all, I'm going to talk a little bit about what the NVU architecture is. I'm going to get into an example a little bit. So this is a little weird. Well, I'll kind of give you, I'll motivate this with an example that we're going to look at. So when I'm talking about the model view update architecture, this is an architecture that I think probably originates with the Elm language, which is why the library is called Elm-ish. It's a different approach to designing a front-end user interface. I mean, I know that some of you do have experiences in using front-end user interfaces, but this is a particular discipline that allows us to do some cool things. It's sort of a very functional way of thinking about front-end software development. So let's take a look at the component. So the view, right, is what you see on the screen. View is usually like HTML or XML or whatever. It's all of the stuff that you see, the buttons, the forms, the pictures, the JPEGs, whatever, what have you. The model is the view represented as data. So typically, the model is a lot smaller. The model doesn't have to have all of your CSS, your styles, or your pictures, or things like that. The model just needs stuff that is unique to a particular view state at a particular time. So the model's a lot smaller. Now, it wouldn't be very interesting to have an app that just has a model that renders to the view. So the model renders to the view using some kind of view engine that translates into HTML. When you interact with the view, in this case, it dispatches a message into an update queue. Now, I think this is what makes MVU special, is that every interaction you have with the view creates a single message and puts it in order into an update queue. You define an update function. And what this does is it holds events, like messages up with the update queue, and updates the model in sequence. The model is immutable. So this is sort of the key thing. What do I mean by saying the model is immutable? The fields in the model aren't updated. Every time the model changes, it blows up the old model and creates a new one. What this means is that since the model is immutable and its members are never changing, you can create an efficient hash function on the model, which means that it's easy for the view engine to detect what is a change to the model so it re-renders the view. It's a very elegant architecture. It allows you to sort of represent all the interactions sequentially, right? So every interaction you have with the page is handled in sequence as opposed to a bunch of different things fighting for state in the front end. And since the model is immutable, I mean, I don't know of another way to have an immutable model without having a single kind of update queue. It really makes the view engine very efficient and very fast renderable, which is pretty great. I want to compare this just to a couple of other standard architectures. So AngularJS is sort of, again, the granddaddy of architectures. There's a typical application architecture called ModelViewViewModel. And what differentiates this, I think, from MVU, is it's not one direction, right? So there's what's called a two-way data binding between the view and the view model. What this means is that every field in the view, whatever it changes, it triggers a change in the view model. Every time the view model changes, it triggers a change in the view. Now, this is nice, for example, because then you don't have to think of a text field as being anything but a texture, right? It's just text. And when you change the text, it changes the view model. Not so bad. I've seen some terrible things in my tape. Basically, what ends up happening is you need to have a sort of dive to your slope. I know. Like, I just read a page. I seem to be like, I've seen horrible things. You're a windman. House, yard, stair. No, so you need to have what's called a digest cycle in AngularJS, which basically verifies that all the changes have updated the view and all the changes have updated the view model. And it goes back and forth and back and forth. I've seen code where it's something like, OK, if the number of digest cycles hit 100, stop and just forget about it. And it's like, that's not cool. So this is actually a really good model for very simple applications, but if you run into trouble when people try to get too tricky, try to implement things. And I've, again, not so great. MVC, probably standard. If you've ever written a front-end application, a modern front-end application, you probably use MVC. MVC is great. I think probably most successful MVU applications are a bit of a hybrid between MVC and MVU. We have a bunch of individual components, which are MVU and then everything is orchestrated using some sort of MVC framework. The big difference here is that instead of having a single message queue, through which all of your interaction passes, you have a bunch of controllers. Each of the controllers interacts with the state in sequence or not necessarily in sequence, but individually. Because you have multiple controllers interacting with the state, they're all pointing to the model, which represents your state. You can't have an immutable model in this case, right? You want to change anything? You can't just throw away the model because this controller is still pointing to the old model. So you lose the immutability of the model. You lose the well-orderedness of the effects, but in some ways you gain, it's a little bit easier to code. Actually, you can be super effective. I mean, I think it's a good architecture. Today I'm just going to be talking about MVU, what kind of MVC talks out there, I'm sure. So are there places where MVU is not appropriate? I mean, we've got these other ones that seem more complicated. So I would say one of the things about MVU, and I'll show you an example, I'll show you some code. It takes seven days. Well, yes. So a text editor, I think you could write a text editor with MVU that the challenge is really this update function. If you have a lot of messages, the update function can get pretty narrowly. And that's where, so it's a good thing and it's a bad thing. I feel like it's a kind of friction in your application that can prevent you from writing things that are way too complicated. But I think in some ways it's good. In other ways, it can just bend. Once your update function gets into a bunch of switch cases, it sort of gets less efficient and it gets harder to manage. I think that's the big thing. Sure. Chad, can you compose the update function? Yes. So yes, you can definitely compose update functions. So there are ways through strategies, but you have to be pretty disciplined. I think that's a good point. Thanks. So let me show you an application. You can take a look really quickly at what this means. So I'm actually gonna run code. See, I'm smart, I actually wrote all the code ahead of time, so I'm not gonna try to live code. And here's my application, it's pretty simple. This looks like a typical business application for your enterprise. You got your hamburger button, you got your home screen and you have an input. It's just a single form input. So I'm gonna put hello world. Now, what happens is when I post this input, it saves it. Actually, if you look, the URL changed. It creates a session for me. So now it's all saved to a nice session. In fact, if I copy this session, if you don't believe me, I open a new webpage. It keeps the text. Nothing fancy, just guarantee that we're actually saving the data in our application. So can anyone in the audience identify what's the model for this application? What are the two pieces of state that we're dealing with? You guys see what they are? Local storage or session storage? No, even lower level than that. Just looking at the application. I'm using the index DB in the application. Yeah. The index input of the auto input. Yeah, so in this case, it's the text input. The button is an event. So the button is an action. The text input is one thing. And the other thing, the secret is the session. Right, so we actually have a session value. So let me show you really quick what the code looks like when we actually look at it in our main file. Is this too small? I mean, it looks good to me. Oh, that's way too big. Oh, what do I do? I think you just need to minimize the console. Yeah, I'll minimize the console and I will press the right button that actually allows me to increase the text size. Is that good? Looks good to me. Okay. So if we look at our model, well, the model is a little bit tricky. It's actually discriminated union because our model consists of a page which is the page that we're at at a given time. That's just what's in the URL. And we have some input, which is our string. That's the string that I typed into Hello World. If you scroll up a little bit, a page is a discriminated union. Now this is the case where F-sharp I think shines because if we're at home, right? We don't have any input. There's nothing in the URL. When we have a session, it's actually a session of a string. This is an example of an F-sharp discriminated union. If you're not familiar with F-sharp discriminated unions are kind of like enums but with some content, right? And in this case, the content is the string and the string is the token that I created when I posted that data. So those are what I would call our nouns and then what our verbs are are the messages, right? So these are the ways we actually interact with the application. Disregard the fact that there are two navigates that's just sort of an artifact of the way the router works in the program. The router just relates the state to the URL route. But the things we can do is we can move from one page to another. We can post some data, right? That was that button. We can set input on our string and we can fetch data. So let me give you an example. So I'm gonna go back to local post 5,000. I'll erase this real quick. So if you look on the console, I'm logging all the commands that are being done at any given time. So a little world. So what's going on is when I'm typing, you can see the set of commands. When I post, it posts. It's a little bit tricky, right? Because I post it redirects to a new page because it's generating a session. We're not already in a session. It navigates to a new page. Forget about that. And then it fetches data from that page. So after we navigate to a page, it has to collect the data from that page and then it sets input to whatever it fetched. So there's a little bit going on here. Maybe this could be simpler if I didn't bother fetching the data, but I didn't really feel like dealing with that particular case. But the idea is that if I were to open a new window, just with this session token, it would navigate to the page, fetch data, and then set input to hello world. So what I really like about this application, this is almost by the way, it's not necessarily Valero. And I'll give you a little bit more on Valero for this slide. One thing I really like about this is that you can see exactly what your interaction and what the nature of your interaction with the software is. And you can see how it updates the page. Is this token session token somewhat of a little bit more actually you mentioned before? No, no, I'm just making up a good. It's this session token could be, it could be a hash of like, no, I'm creating that. And I can kind of show you how I'm creating that with the update function. But it's basically the idea is that if someone's logged into an application or if someone's in an application, you can't save their data until they're logged in, right? Because you don't have some way to retrieve that data. So that's why I created the token before I, if, so if you post, just to give you an example, if I change the data and I post, it doesn't actually, it doesn't actually create the new token. It just saves the data, the database. So it doesn't go through all that rigmarole. Let me show you the update function. Quickly, it's not too bad. So in this case, I realized it's sort of F sharp syntax. And if you're not super used to F sharp, it might be a little bit funny to read, but this is a function, it's curried. So update is the function, JS message and model are the arguments. The message is the event that comes in and the model is the current state of the application. JS is just a reference to the JavaScript code that I'm using to save the data. And what it does is it's basically a glorified switch case data, where if we wanna navigate to a page, it updates the model with that page, which is page equal page. And it has a side effect. I'm gonna talk about side effects in a little bit, but once it navigates to the page, it then emits a secondary message which says fetch the data, and when you navigate to the page, you're gonna be able to get the data that belongs to that page. Set input, it just sets the input to the text that you typed, redirect again, just moves you to a different page. Now here's the logic with the session token. And here we have a switch case, inside of a switch case, right? So it checks to see what the model, what page we're on. So if we're in a session already, it just writes to the database. If we're at home, it creates a new token, and then it writes to the database, and then it redirects as the side effect. And it actually is pretty similar. So all it does is it just, it leaves the model as it is, but then it just reads from the database, and then it calls as a side effect, the set input function. So as you can see the sort of the philosophy is that when I interact with the application, it changes the model, but then it might send off some passgative events happening after that. But we can keep track of that passgative events really well, because we can just walk into the console, we can see exactly what's happening in the application. And that kind of clarity, it really makes it for me, a lot of users that debug the front end, I wanna make sure we don't have a fill the screen while you want, we have an app, okay, good, I wanna make sure that I've got the same, this thing's in chat. The view is definitely, I think one of the cool innovations. This is a little bit more inside baseball, but what it is, is it's something called a type provider, F-sharp. So what it does is it reads in an HTML template page turns it into a type, and then you can call that type, and go down to where it's called. It gives you some, it gives you what are called, just sort of like holes in the page where you can insert templating. That's a pretty cool templating engine. But yeah, I mean, I guess the main thing is it's just an app that's running in WebAssembly. There's a little bit of JavaScript so I can talk to the database, but it's pretty simple, right? Yeah, like, I said this app sucks. Have you ever seen a post under a, I mean, come on. It's like, you come to this app, you click hello world, and you have to click post to save it, right? If I come to the website, if I come here and I go to local host, sorry, I know, I get animated. It's really just sort of a thing. If I come here and I start typing, it was the best of times, it was the worst of times, and then I changed, it's all gone, right? So I feel like modern web applications need to have a little bit more, right? They need to have a little bit more interaction with state and they need to have side effects. When you interact with the application, it's not just enough to say like, well, I'm gonna type in some code here and I'll have to remember to save it. Maybe there'll be like a validation step that gets really frustrating. It sort of feels like, you know, web from 15 years ago, right? The very start of sort of web forms and these kinds of things. So we want to give a better experience to our users, and this is where I think the Bolero was really cool, because we can improve this experience in ways I think are pretty clever and make good use of, yeah, short language. So I'm gonna talk a little bit about the next part of the talk, which involves side effects. So side effect is any action, I don't know if this is the common definition, but the way I think of it is any action is not directly dispatched by the user, right? I can click on something and there might be something that happens after that and something that happens after that, but I wasn't the one who, I just clicked off this chain reaction. And there's a whole lot of examples in web programming where you have to have side effects to have good user experience. When I post an HTTP call to the server, I have to wait for the message to come back, right? So I have a side effect, which is when the HTTP call completes, I need to do something with that message, right? So it's not enough for me to click that post button. I also have to do something when that HTTP call comes back. So we saw how Bolero manages that kind of side effect. It just has sort of like a built-in tool that waits for that message to come back and it gives you something that you can, you know, do what it does when the message does come back. But that's not enough, right? So sometimes you don't wanna just have a post, you might wanna have something called a poll. So polling pattern is you might admit a post call every so many seconds and it might not be complete. Like, so if you have a long-running task on your server, you might wanna keep pinging the server and stop pinging when you get a response back that says, okay, we're done, right? This is a common pattern. It's a good way to implement some, you know, handle long-running tasks if you're not dealing with like web sockets. It's just, you know, one way to deal with that. And most modern web applications, you probably have to implement it at some point. A debounce, so does anyone here know what a debounce is? When you're typing, right? You want something to happen when the interaction stops. And this is kind of what I was talking about in my application is that when I'm typing, I don't want the, you know, I don't want to have to push a separate button for the application to save, right? That's so lazy. I'm not lazy. Look, come on. What is it like, you know? Yes, you are. It's the millennials, right? It's not me. If they don't say, this is the problem of having people who use my software and have my email address and my team's messages, they still get angry. Yeah, they get angry. They get real angry. They're like, I just typed in a table of two cities and it's gone. Can you just cover it for me? Like, no, because I didn't write a debounce. Well, we also didn't speak to it. But that's what I'm saying. I think the biggest thing would be there's an application you have to propose because I don't see a... Spinner. Yeah, yeah. That's a nice part of it. You always show a spinner. So people don't think you've got a C7. Yeah, spinner is good. Spinner is a good example. What's up? No, I was saying, like, when it comes to your IUS, you're always going to have some sort of, like, reaction to the questions. Otherwise, people will be like, okay, did it submit? Did it not? Yeah. Is it doing something? Yeah. No, absolutely. I mean, it's pressed button, yeah. Yeah, so there's some places where you want to have some reaction, and some places where you want it to happen automatically. I mean, there are definitely places where you want things to happen in the background because when people are writing Google Docs these days, you don't have to save. I wish that you just put in a big save button, though, that I could click. And I would feel so much more... Kind of like the intersection or across the street, but... I mean, look, I type in writer, it auto saves. I still type control X, control S, which is the Emacs word for save every two minutes. So I realized this. I'm not, you know, I'm just saying that... Yeah, Kevin pointed out just on a chat that the network could go down, so auto save is good. Yeah, auto save is good. And the cool thing about F-Sharp, this is really something about F-Sharp L-Mesh, but I think it's... I think it works really well in Valero is that we can use patterns like F-Sharp style patterns to implement some of these features and they look pretty cool. So let me show you another example of something. I don't know. You know what, I made a big deal about how awful that first one was. I probably shouldn't have because I don't want you guys to complain about my improvement, but it's actually, I think it's an improvement. We are either improving. I don't see any difference. You don't see any difference? Where'd the post button go? Oh, yeah. So if I just type here, world. And I wait a second. I know, there probably should be a post button somewhere for those of us who like post buttons. But this is just sort of fancy, right? So when I stop typing, I'll pull up the, I'll pull this up again. Where's the spinner? You don't need a spinner, man. Well, we're going, we don't need spares. So if I type world, it sets input and then it posts, it waits a little bit. Yeah. It's back to the future. What's that? It's back to the future. Yeah, it's back to the future. Yeah, exactly. So I think this is an improvement. And let me show you kind of how I manage this from an architectural point of view. So you can see, I guess my philosophy is you don't want to replace, you don't want to do anything to the events that are coming in from your user, because you want to capture those. That's important. That's how the user's interacting with the tool. And you don't want things that are going to capture what's actually updating your model, because you want to say like, I want to log all the messages that have updated my model. But you can put something kind of in the middle, which I'm going to call an agent, which is something that lives there. And sometimes when something gets on the update queue, the update function might kick out an agent. And the agent is able to take commands from the update function and it's able to send commands back. So let me give you an example of how this works in my home. So I haven't really changed that much. In fact, if we were to compare this with the original code, the only difference here is my set input before had no side effect. It just would update the input. Now it has a side effect. And what it does is it performs a debattance, pretty reasonable. So this is a nice little plugin architecture for this application. And what I'm doing here is I'm using another Fsharp feature. So we've sort of used the Fsharp feature of discriminating union. Now I'm using an Fsharp feature called a Mailbox processor. And these are super cool. They're like these little threads that you can create in Fsharp. And all I'm doing is I'm creating what I call a delay thread. So all the delay thread does is it's a recursive loop. So for those of us who like recursion, pretty cool. It accepts messages in an inbox. This is this inbox receive. And it doesn't do anything until it gets a message. When it receives a message, it disposes the old timer. If there were a timer there before, and it creates a new timer which says, don't submit this for one second. What this means is if I'm typing and typing and typing, it's sending messages to this inbox and it's just blowing up all the old timers. And the timer doesn't actually go off until I stop typing for a certain amount of time. Otherwise it's gonna dispose all those timers. And it's a really nice little recursive function. And the cool thing about this architecture is because it's acting as an agent. It's sort of out of process from our update loop. So it's just sort of when we just added in as a side effect and it sends the message to this update loop and pulls it back. I also have an implementation of polling, which is pretty similar. It just kind of repeats the poll until it's told the stop. So these are a little robots living in the background that are sort of doing things with the update queue, but not really impacting the state of the application as a whole, but making things nicer. Which I think is pretty cool. And to me, this speaks to the extensibility of the Bolero framework or anything you're working with that sharp. I think it'd be very hard to implement something like this. Not maybe it would be that hard to implement something like this in TypeScript, but it wouldn't be as pretty. So how are those being called again? So the way they're being called is, I'll show you how it's being put into the, hello. I don't know. What's your programming? So the way it works is remember the way the update function works is it takes the message and the current state of the model. Then it returns an updated model. That's this one. So we're just updating the model. We're just saying input is now the new input. And then it has a side effect. I'm not gonna go quite into the details of how this is implemented, but essentially what it means is that this debounce is a long-living thread that's actually created in the, I'm just creating it in the main loop of the program. So I only have one debounce for every text field. But essentially what it means is what this side effect does is when I when I put a message onto the queue, it sends a message to that thread. The thread creates a timer and it disposes of the previous timer. So the nice thing about this is that if I had typed something and it set a timer off to say, like, okay, send this to the server in one second, we're not gonna get a whole bunch of traffic at the server. It's not just a delay. It disposes of that and says, oh, new timer, just go with this one. So the only timer that fires logically is the last one before the timer. Thank you, yeah. So I think the idea here is that what we want is we want an architecture where we can treat some of these side effects like plugins or if you're like my audience at the dotnet users group, middleware, which I know that's how dotnet developers think is in terms of middleware, but it's pretty neat. I have a question. Yeah, go ahead. So every time that you ask to perform this debounce, you're creating and then disposing new objects. Yes, the timer is the only thing that I'm creating a disposal. Yeah. So why not make the timer part of the model itself? And when you set input, it's model with input equals input, timer equals zero. So then I would update the timer every time. I don't know if the dotnet API allows me to update the timeout in a timer, it's possible. But I mean, it's not a bad idea. So the main thing is if you think about it this agent, all it's doing is it's a single thread that's managing a lifecycle of a bunch of timers and dotnet. So I'm not, I think, I like the idea, but I'm not entirely sure how to, I think we need a separate thread to manage the lifecycle of the timers, but I'm not sure. Because we need a reference to the timers somewhere in the program. And it's easier to create a reference to the manager of the timers, which is the devout solution. But yeah, I see what you're saying. It makes sense. I'd be curious if you could come up with a simplification that would be super cool. Maybe we can talk about it. Yeah, well, I was just thinking that if you could bake it into the model somehow, then you keep it a mutable model, right? You know, you keep a... So yes, so there are other implementations. So let's see. So there are implementations of devouts where you create a devouts message. And then, so I think that's where you're getting at, which is totally reasonable. In fact, my understanding is the founder of Elm much prefers that approach where you keep track of the devouts as like a message, which is like devouts of timer. Does that make sense? So you have the content in your devouts, the timer. Yeah, yeah. Now that works. I guess the trade-off there is that your messages get more complicated. So I think it's good in some ways to do that, but there is a trade-off, which is that you have to deal with the devouts operator and your messages. And for me, I don't know, I kind of like not having to deal with that. I think it's sort of a constant battle of, you know, trying to keep the number of messages that you have to deal with small, but people disagree with me. So I think your point is a lot of people agree with you. Kevin had his hand off. Kevin, did you have a question? I don't know, hey, that was a minute ago. Oh yeah, I just wanted to kind of chime in on this question about whether or not to put a timer in the model. And my understanding of the timer, and maybe this isn't correct for F sharp timers, but I think there's some internal state to that timer that's being updated kind of independently of, you know, the Elmish event loop, right? So you're going to end up with your model not actually being immutable because you now have this mutable thing that is living in there. So that would be my reason for not putting it in there. Yeah, yeah, and I think there's other things you can do. That's, yeah, that's a really good point. I think there are tricks. So there's actually a lot of literature out there because a lot of people have used Elm or Elmish and have discovered that they really want a feature like this, I'm not the only person, but this is sort of the solution that I like. But yeah, no, that's a really good point. I like that. So finally, by last example, is one of the cool things about the Elm architecture is that it enables, sorry, I have to hold up the slides. I think I can read event sourcing without the market. For whatever reason, Ryder's given me trouble today. Event sourcing. So this is a very popular pattern in modern architecture design. And the cool thing about the Elm architecture for the MBU architecture, the Elmish architecture, how you like it is it basically enables this architecture from the start. So the philosophy behind event sourcing, I'm probably gonna pause it because I don't think super hard about these things, is instead of your typical interaction where you save state to a database and update state in a database, your typical kind of cred application, you only capture the actions on the data and you list the actions in an event-only database. So the cool thing about this is that I can look at the sequence of actions that have been applied to the data and I can roll back that sequence of actions to any point in time or roll it forward to any point in time and still have an accurate value of the state. We're never losing data if we just have a consistent list of actions and we can reproduce the data at any state in time by just rerunning those actions that are applied to the data. So this architecture works so well with that. Because the fact is we are updating the model through a sequence of actions, the model's only ever updated through the sequence of actions and if we just log the messages, we basically get event sourcing for free. So this is great again for audit, I mentioned for time travel and data consistency. When you have an asynchronous system, it's useful to be able to understand the sequence of operations to get the data to a certain point in time. This is super important, especially if you have back ups or databases in multiple locations. They have a single source of truth, which is this data got to this point only by this particular series updates and changes. So as with every, I feel like every good architecture demo has to end up with a time travel example. And I'll say this, I wrote this myself. It wasn't very hard to write. Literally, I just logged the events and I created my time travel example. So let's see how it works. I can start typing in here, I added buttons back, but I can say, hello world, okay. It saves the data here, puts me in my session. I'm gonna type a little bit more. Hello, HFPUG, okay. Now, what I can do is click back. It puts me into time travel, and I can follow back all the sequence of state. Now there's one funny thing, is the time travel only starts once I'm in a session, because that's the point when I start logging. So I missed out all the stuff that I typed in before it put me into the session, but everything after I typed in hello world is all in there and I can navigate through the code. Why is this cool? My company pays tons of money to an application called Full Story, which keeps track of user sessions and basically just looks at the sequence of clicks people do on a particular website. And this architecture gives it to you for free. That's pretty cool. But thanks, everyone. Love to answer some questions and to keep it about an hour, so. Questions, folks up there? Yeah, I have a question about the routing. I noticed that you showed very briefly a couple of routes and one of them had a URL param with it. And it was just, I noticed that the route itself didn't mention the param. So I'm curious how do routes work with multiple params or you might have session slash ID slash something else slash other ID. And I'm just curious what those things look like. No, that's a really good question. So right now in this version of Bolero, it actually changes the author. Again, I mean, I think it's made a really nice product and he's continuing to update it. Updated the ability to handle what are called query parameters in the URL in the templating. I actually had to do that in my own code. So I just wrote some code that did that, but now I can use the good code. The way that typically works is that if you have one of these decorators, the router recognizes this decorator as corresponding to the route slash just nothing. If you have a decorator and you have a discriminated union which has some content in this case, this content is string. Well, first of all, the content can only be a string or a number, but then it recognizes it as being part of the URL, basically the next step in the URL. I could show you too, the way the router is defined. It, in this case, it's using something called router infer which parses the decorators on the routes and it earns the, it gives you a way to interpret the state. It basically gives you the ability to interpret the state as a URL and map the URL to a part of the state. So that's kind of what's going on here. It says the, if you see, yeah, so if you see the URL, you apply, it means to apply navigate. If the URL changes, you have to apply the navigate function to it. And if the state changes, then I guess the URL corresponds to the page. I think I'm making a hash of the answer, but does that kind of make sense? Yeah, thank you. Cool. It only takes a string or an int and so you can't use a discriminated union in the navigation or in that page. Yeah. The router. Yeah, so right now the router is limited to, you can either have a string route or an int route. And I think the reason is, is because typically URLs, I think he just doesn't want to do a lot of perversing of, you know, data types to strings that would fit anywhere on a router, which I feel you, man. That's, Yeah. Just to clarify, like you can only have like a single string or a single integer in up to your route. You know what? I think, I think if you do string time string, I'll have to remember. But I think if you have a tuple, you can potentially have one string and then like string one slash string two. So you put that, cool. Thanks. Session slash something, just something slash something. You can, but not with the templating integer. You can, so in my production code, I have something that more manually maps the URLs to the state, which is fine. I mean, it just parses the URL and it maps it to a state. This is just sort of what you get for, if you just want to use the decorator. But really, yeah, what you need is, you need some function that when the view changes, it needs to admit a message, right? So when the URL changes, it needs to admit a message. And when the state changes, it needs to be able to recover the URL from the model. And so that's all you really have to do. This just does it for you automatically. So are you now using this in production? Not this app. But yes, this is a great work. It's great. A load is made. Yeah, I'm using a slightly more complicated method. It's not, I mean, it's not crazy. It was, it's really a replacement for part of our AngularJS staff. And it was pretty quick to write and Anthony worked on it too. So yeah, you've got it up pretty quickly. It's working and you're happy with it and it solves the problem. Solves the problem and front-end's disposable. So if we hate it six months from now, I don't think we will know. I really like it. And I like working with Blazor. So yeah. I think it's also worth re-emphasizing a point that you made at the beginning that like, you know, this is all just F-shark. So, you know, we just had this stuff on our computers already. We didn't have to download anything. We didn't have to manage Node. And we didn't have to touch JavaScript. I mean, there was a couple of cases, but not the JavaScript ecosystem. The most painful part of creating this website for me was getting Tailwind to run. Oh, Tailwind. Yeah, that's Tailwind. That's what I'm saying. Is that I just getting Tailwind to build was a pain, but yeah, the F-shark was a joy. I just, you know. Yeah, you'll get the Tailwind issue in that one, I'm sure you'll use it. Well, I'm gonna thank you and then we'll stop the recording and continue our discussion. Yeah. Thank you so much. Thank you very much. Thanks everyone. Thanks everyone online. I'm usually one of you, but not this time. Cool. I'm just gonna get just a reminder of just if you get some before we start the after-party. And again, if you get the chance to just give us some feedback, scan this list and then we'll again, give us the headcount also, how are we doing as far as hosting? So yeah, if you get a chance, please do this. One, two. Is there time for like one more question? Yeah, of course, I'll just start sharing. Yeah, I'm curious about the build, like for that demo app that you were showing, is it just like a static files that gets generated? Like there's no server component to it. Oh, so that's actually a really good question. So we have, there's two options for laser apps. There's what's called the Blazor server. And there's Blazor Pure Web Assembly. So in this case, I used Pure Web Assembly because I wanted something that could just run in the browser. Normally, I think just for a proof of concept, I didn't want to have a server component. Though app use and production uses Blazor server. So there's actually a server side which talks to the front end using, gosh, I think Elm Remoting, Elmish Remoting or something like that. But yeah, what this does is it creates a, all it does is it creates a web assembly package and it hosts the web assembly in the browser. So it's just a web server that hosts the web assembly is one of the packages that's set to the browser and the browser handles that. If the browser loads the web assembly through an HTML landing page. Awesome. Yeah, that's actually kind of what I was hoping for. So I mentioned at the start, I'm working on my own programming language. I want to create like a little playground area on a website, but I just want to post it statically without having to set up a server. Yes. I just want to apologize for y'all for having to look at my countertop. I just realized that my camera was using the wrong camera. One last question, in your project, do you have any C-sharp that is interacting with your F-sharp or like any libraries that are using C-sharp under the hood? I'm just curious about like, if that makes things more complicated or not. Let me think. So in this project, no, this project was specifically only wanted to use F-sharp in my production application on using a Power BI SDK, which is downloaded from Nougat, but that's a C-sharp package. I mean, it's .NET, you can use C-sharp packages in F-sharp, and I certainly do. In other applications that I use, we use a mixture of C-sharp and F-sharp, but yeah, I mean, the .NET SDK works fine, any .NET works well. There are a few issues that I've found in the WebAssembly component, where not everything compiles nicely to WebAssembly, but most libraries do, and most C-sharp libraries actually work pretty well. So the Power BI library works fine. Which was the end? It didn't compile well. What's that? Which libraries didn't compile well? So there's a weird error when you compile the F-sharp computation expressions, sorry, the F-sharp mailbox processors, where it doesn't allow you to have, it doesn't allow you to put a timeout on the mailbox processor. So sometimes you can have the inbox have a timeout, and when it runs out of the timeout, it's inside baseball, but basically you can have the inbox wait for a message, and then have it cycle the loop if it doesn't get a message. That didn't work in WebAssembly. I feel like I haven't tried to load anything more complicated, like I know that there's a lot of F-sharp libraries, like Parsec is a library that I like, it's just a high performance parser that tends to have, depends on a lot of sort of internal C-based libraries, and those don't like to compile well to WebAssembly, but yeah, I don't know. I think for the most part it works pretty well. So actually, hold on, let's go ahead and stop the recording, and then we're going to just keep talking. And Kevin says he's just an F-parsec. I love F-parsec, but I guess, there's a group of people trying to update it, so it's in current development, so maybe it works, I haven't tried it. That's just the library that I've had trouble deploying to non-preparation. I've had trouble deploying to different targets before.