 Hey there, thanks for joining me. We're gonna be talking about asynchronous JavaScript and PureScript for Haskellers today. My name is Ben Hart. I'm director of Cardano Operations at mlabs.city. We're currently hiring for Haskell, PureScript, and Rust at all levels from intern, entry-level, senior, and management. Take a look at our website mlabs.city. So I personally come from four years of Haskell and PureScript via functional full stack JavaScript, and I'm from Toronto. Let's let's drive in. So what are we even doing? We're gonna examine some asynchronous primitives in JavaScript with callbacks. We're gonna take a look at more modern approaches like promises and async await. We're gonna take a look at pure script abstractions in the effect and af monad. We're gonna learn just a little bit about fibers, which are almost threads. So examples 1 through 3, we're gonna take a look at sort of simple event emitters. So this is a way to sort of reason about how JavaScript asynchronous sort of appears to work. So this is synchronous programming, but it can build up into an asynchronous kind of API or essentially can be used to sort of reason about the way that the browser allows us to do asynchronous programming or node for that matter. So here we have sort of an emitter object. It's got this idea of some listeners, which is a queue of functions that will grow as we add them with on event. So we're gonna add listeners and then we're gonna fire off a listener. So this is where we get into sort of asynchronous appearing behavior where we can set up some events that then happen later on so we can reason about these computations a little bit differently. We can sort of store some computation elsewhere and then fire it off. But again, this is still synchronous, right? We could sort of step through this code really easily without worrying too much about missing pieces, at least until we get into things that are out and stored in different modules where we have to reason about things a little differently. So this is a really basic system. Let's go ahead and run this sort of asynchronous foundation. So here we can see we have this idea of like starting the action, listener attached, and then even though we say pow up here, it doesn't happen until we sort of kick off this event, okay? So it's a really simple sort of scaffolding out of of this computation that's gonna happen later on and then we can kick it off in a much easier way without defining all of the computations got to happen right at the moment when it has to happen, okay? So next we're gonna move ahead so we'll extend it and we can actually add, we can add a lot of things to this actually, if we flip ahead, we can extend it to include event types, which is what we've done here, but we can also extend to different types of observable data and we can extend it to do all kinds of other things. State management, it's a good intuition for how DOM event listeners operate, so if you want to add a button to a page and have events occur, it's also sort of how Ajax requests and networking requests are gonna work. So here we have another example. We've got these two different event types and I have sort of intentionally switched their order, so now we can we can sort of fire those off in in different orders based on labeled events. So our what used to be just one queue is now a map of queues to event types, okay? And then we can have another sort of version of it where instead we hold some some data and every time we change the data, we make that data available to some functions for state management, okay? So this is observable data. You can see we also optionally give the ability to reference the previous value using variadic functions here. So because we don't have a fixed number of arguments to our function type in JavaScript, we're able to to pull these kinds of tricks. These will get ironed out later on, but for now there are some conveniences that we can pull from this. So let's go ahead and run that example. And you can see as soon as we change this initial value of hello to bob, we get first the first listener going off and logging out the original value hello. And then a second listener fires, which is going to give you the new value. So these could be supplying them out to other callbacks. They could be running network calls themselves and you can get this notion of sort of a chain of functions that call each other, building up a call stack, but potentially with asynchronous steps where you're not adding things to the call stack immediately. So this brings us to sort of a fundamental problem. JavaScript is single threaded usually. Worker threads don't count. They're a bit of a newer addition. But what I'm talking about is the fact that JavaScript runs with an event loop. And when an event is triggered, there's a call to an event listener that's placed in a queue kind of waiting for the event to resolve. And once the call stack is cleared of synchronous behaviors, the JavaScript runtime is going to start to execute listener calls from the queue. So it's sort of very similar to this. But the reality is that this emitter is obscured. It's implemented off in the browser, maybe in C++. It's in the node core. It's part of the web APIs that you think of as sort of the JavaScript base library. All right. So there's a really great talk on this that I can never do justice to. It's called what the heck is the event loop anyway from Philip Roberts. And it essentially goes through just this with a lot more demonstration. So if you're interested in knowing a lot more about how the event loop works, I really recommend that as a great starting place. Now, there's also some modern abstractions that have been added on to help us prevent what's called callback hell. This idea like maybe we have so many listeners. And additionally, the old syntax for functions was very big and then relatively clunky relative to these arrow functions that we're using. So we came up with promises. They're sort of a wrapper for unfinished computations. They can't be canceled once you start them off. So here we're using fetch, which is an API for doing network calls. So we're calling off to this AI age predictor based on your name. So there's a difference between this example and the previous examples. Here we could trace through all the actions and never lose track of sort of where the JavaScript interpreter might be operating and how it might evaluate out. But here we get to a point where we call fetch and we'll very quickly get confused because the fetch computation doesn't wait for us to call fire on any of these. It kicks off immediately through a browser API. So this returns a promise here, which we then called .thenon. But the computation is sort of running in the background in another thread that we don't directly control. And when that thread completes and receives its response, this is going to be kicked off and then likewise this is going to be kicked off. So we have this sort of chain of events and if you squint, this is going to look a lot like a monad and you don't have to squint that hard. This actually kicked off as this API was being discussed. There was a hot debate about adding features like monad to JavaScript. However, there is some convenience here because you don't really have to think about whether you're calling bind or map on this. You can return promises or not promises. And that was determined to be the more sort of JavaScript friendly API. Now we also get some pleasantness that looks a little bit like do notation. So we get async await. And if you sort of equals await, if you squint that into an arrow, you might imagine this looks quite a bit like Haskell's do notation and you wouldn't be wrong. The kicker because it is just like do notation is async actually denotes that your return value gets wrapped in a promise. So then you can go ahead and catch for errors at the end just like you might catch some monad say that has monad catch or monad throw. So we have these sort of functional like types of expressions where we can sort of set up chains of computation and also set up binding do notation. But like I said, it's not cancelable. We haven't officially created monads or functors in JavaScript. And this is where the next great example comes from because there's this great library called future. And future allows you to wrap up promises with a different type, which does implement monads and functors and bifunctors and alternative and all kinds of other abstractions that you might be familiar with. So if you are using JavaScript and you cannot use pure script, this is a great system to use. It also has certain API helpers like enforcing that you supply an error handler before you get to unwrap and get your actual value out. You can also set up cancellation as well. So there's a lot of great things just to get out of the Flutures library. There's also a way to do async await like syntax using iterators. So there's a lot of great options for you with Fluture. But the main problem that you might have is that Flutures errors, they're great type errors, but they are at runtime. Which brings us to pure script. So with pure script, we are going to be talking about three different techniques. The effect monad promises and their relationship to aff as well as fibers. We're going to be taking a look at some examples with fetch and affjacks. So let's just move over to the examples. I've created a couple of simple wrappers here. I've created two wrappers. One is it uses the fetch API just so that we're comparing apples to apples. But this fetch API, it's going to essentially just take a callback and call our callback. So there's not a whole lot going on here. It allows us to just sort of have a callback like interface and pure script. You'll also notice there's this just nullary function. So we have currying here. We get the URL. We get a callback. And then this nullary function is an effect. So when you talk about an effect in pure script, we're just talking about a lazy value, a function that is deferred. Now we've also got this one for promises. So this one is going to return an effect with a promise inside. And we can change promises to affs and affs to promises. So we'll talk a little bit about that. Pure script has two different types of effects that used to have many more, but we've mostly gotten rid of them. So we can return just a promise and say, oh, we've got the promise. But there's not really great tools for unwrapping a promise. You'll mostly want to use aff. Unless you're returning it for a JavaScript user, maybe in a library. So what we've actually got here, we've got first just a callback-based API where we're using our effect call. So we just supply a callback there. Let's go ahead and run that. And we get some compiler output. And then we actually get the effect itself running. And when we get a result, we'll get the callback fired. It's stalling out for me. So I'll just move ahead. So the next thing that we can do is we can set up an aff. So here we're getting the result as an aff. And what we've done, we've got this fetch function that we've implemented in PureScript just using a promise helper to unwrap the promise and supply it as an aff. Aff gives us all of the powers of functors, moanads, bifunctors, omai, and it's cancelable. All of the great features of Fluture, however, we're in a PureScript context. So we can kind of reason about our types at compile time rather than at runtime. Okay, let's go ahead and run that. Hey, and there's our output. So again, we're just outputting text as things land. There's not a whole lot else going on. We're just calling log. All right. So now we've got these sort of basic examples. What I wanted to do next was show a PureScript native example. So let me just come down here. This is affjax on comment, the code that runs it. So affjax is the Ajax library for PureScript. So we're returning an aff. We can do a native request. This has sort of full type safe setup for your request. So here we can do requests with a get method given response format. So you might be anticipating a string response or a JSON response or some other response. You can actually set it up to do some of the parsing for you if you like. In this case, I'm just leaving it as a string. So we're not getting too much into the parsing world. But it's the same request, essentially. So we can run this as well. So that's sort of the native way to approach this. There is also a shortened helper just called get, which allows you to use much less of this. But that is the basic idea of an affjax usage. So one thing that you'll note about affjax, and it's something that is common across quite a few PureScript libraries, is it's distributed across multiple modules for typical usage. So I'd say this is sort of a design principle across PureScript is it's very, very modularized. So just be prepared for that, that you'll be jumping around all of the different modules in that library, even compared to Haskell. It's quite modular. All right. So we talked about fetch, we talked about affjax. I do want to spend some time with fibers. So fibers are a really interesting feature. Fibers essentially allow you to take affs and control these forked computations. So you can start to reason about aff as a unit of asynchronous computation. But if fiber is really a chain of asynchronous computations that you could then call various methods on. So you get some reason about it. You can see like fork aff I think is probably the most descriptive intuitive function that you might see coming from Haskell because it works exactly like fork IO. So you'll get IDs, you'll get cancellation capabilities. And you can join fibers, kill fibers, you can make a fiber invincible, or you can make an aff invincible. So there's lots of different capabilities that you can set up. For fibers and for working with complex asynchronous computations. All right. So just to summarize PeerScript's approach to asynchronous computations, you get lots of different type safe wrappers to define the outputs of unfinished computations. And you also get control over synchronicity and asynchronous using effect and aff. So effect is for all synchronous effects, which is why you have to supply callbacks everywhere. And aff is for asynchronous effect. So you don't have to think about supplying callbacks. You can just operate on your data without too much concern about what is synchronous or asynchronous, although it is clear in your notation, which one is which, because we're in due notation. You also get type level knowledge of blocking operations, which is sort of, I guess, the collaried to the above. You get abstractions for chains of computations, which follows familiar type class interfaces and laws. And you get the flexibility of flutures, but at compile time. And you get due notation. So let's go take a look at a production example. I want to show you a library that I've been working on. We're pulling in a bunch of WebSocket things. We're using the WS library in JavaScript. So we've pulled in a bunch of foreign imports, which are just wrappers around various library calls. And what we can set up through a whole bunch of layers around the specific WebSocket interface that we're using is this query M monad, which allows us to just have a reader T over some config and an F. And this essentially allows us to set up some guarantees through adding and removing listeners sort of all over the place as queries go out and come back to abstract over the fact that WebSockets don't give you good request response guarantees. And so this sort of builds up a dynamic dispatch system. So you can see we do quite a bit here to build up a type safe dynamic dispatch system. We build up listener sets and methods for adding and removing to a listener set. But through this system, we actually have a very nice API for working with WebSockets, where you don't have to concern yourself with like a mother listener that listens for everything that this WebSocket could possibly spit out. We have it, but we have it sort of buried in some async helpers. And we can abstract over it. So this is a really, really strong use of the F monad, I think, in pure script. I encourage you to take a close look at this. This is the Cardano browser TX library on the Plutonomicon. So this is one that I've worked on with the team at MLabs. Well, that's it. Thank you for watching. There should be a Q&A session right after this if I was able to be there in person. Just a reminder, MLabs is hiring Haskell, PureScript, and Rust developers at all levels. We're a premier blockchain consultancy working on Cardano, Solana, and Polkadot. Please take a look at MLabs.city for more details. And I will see you at the Q&A. Which PureScript async handling approach is mostly used in popular libraries in production? And am I using the Xmonad? I am using Xmonad. Which PureScript handling approach, async handling approach is most popular? I would say AF. I would say if you're going to get, if you're going to do with any library that's doing network requests and quite a few others, you'll see AF first and foremost, I would say. How can code using AF coexist with something that uses promises or some other API? Yeah, let me, let me share my screen quick because I just want to show you to a library. So once my screen settles down from Zoom, being crazy with it, there is this great control.promise module if you're on pursuit that lets you freely convert to and from promises and AF. So to and from AF are all here. I'm just going to flip back over to the chat as well. Yeah, so that way you can sort of pass things back and forth from promises to AF, AF to promises. So especially if you're dealing with a JavaScript library returning promises, super, super common these days, you can flip back over to an AF for the easily. Does AF support cancelling a promise? If so, how? You know that I have not dug into the specific way it cancels a promise. You certainly get a cancel method for any AF, but let's take a look over here. I don't actually see cancelers that get spit out in this library. So it may be doing something just a little bit different than what you'll see over in the AF library. So the AF library has a number of constructors to sort of build up an AF, but you'll see they always return like an effect with a canceler. I actually don't get that here. So it might be that you can't generically just get a canceler out. It may be that it modifies your promise callback in some way, but you could always take a look at the source, I suppose. It's going to take me a while to parse that. I'm going to go with right now they're not easily cancelable right out of the box, but I encourage you to take a look at the docs on that one. Is there a significant performance difference between using different handling approaches? I don't notice a significant performance difference, but certainly there is going to be some penalties. Some are going to be better than others. I don't think one is severely worse than the other, but your mileage may vary. There you go. Robert just confirms that promises can't be canceled because they come from JS land. Thank you, Robert. Does it make sense to write custom FFIs around HTTP calls like fetch? I wouldn't really do that in production. I would just reach for AFTRAX. If you really don't like the way AFTRAX is doing something, you do have the option. Like I just was, I don't think there's anything super wrong with it, but in general I do prefer working with AFTRAX. At least working from PureScript. Working in JavaScript, I really prefer to work with Futures. And again, there's a toFutureFromFuture sort of set of methods in the Futures library to go from a promise to a future. How does it compare with async-await in JavaScript? I'm not quite sure what you mean by it on a pump. Oh, okay. So how does AF compare with async-await in JavaScript? I would say they're really comparable in terms of the sort of making asynchronous programming appear synchronous. I see there's a back and forth also about promises and cancelling. We'll get to that. But I would say AF is really comparable to async-await, but with more type safety. Certainly because we're in sort of a Haskell-like language, we're in PureScript, it's going to force us to make sure that our binds and our lets are sort of separated out. Whereas binds and map calls and lets with a promise or async-await, if you put an await call in a place where you don't need it, I don't think anything bad happens, at least not last I checked. So it looks like there's this abort controllers, which I've actually not heard of, but not every promise supports abort controllers. Is AFJAX using Fetch or the old Ajax APIs? AFJAX does use older Ajax APIs. And in fact, it uses the XHR2 library, I believe in Node, and the XHR HTTP request from the browser. So it is an extremely old Ajax API, which gives you compatibility everywhere. And it means you're using an API that is very highly optimized because it's been around forever. Thank you very much, everyone. I suppose that's all questions. Take care. Have a great day. Thanks for watching.