scale.bythebay.io: John A. De Goes, The Design of the Scalaz 8 Effect System
Recording: scale.bythebay.io: John A. De Goes, The Design of the Scalaz 8 Effect System
you thank you welcome all I'm John digos and today I'm going to be speaking about the design of the new Scala 8 effects system that I've been working on for quite some time I'll give you a brief intro to what this thing is about so hopefully entice you not to leave in the middle of my presentation before I get to the good parts and then I'm going to give you a tour of the work that I've done on this new effects system for scholars at 8 and then I'll show you some benchmarks and other types of things so how does this compare with the other stuff out there that you may already be using from a performance and safety and expressiveness standpoint and then finally I'll I'll wrap up so about me I program with functions and in fact I contribute functions to open source projects and I start companies powered by functions I love functional programming I was about to walk away from programming forever before I discovered functional programming and then I discovered this and Wow I found a world in which I could actually reason about the software that I wrote and I could build things that I was really proud of and that just blew me away so yes I am one of those functional programming zealots that you probably read about in fact let's be honest you probably have read about me yes I wrote this article on removing if statements from all your code I wrote this one in which I argued against using data structures and the I called descriptive variable names a code smell I may have said something about Twitter once or twice sorry about that Twitter can i but get my blue blue check verification thing now please pretty please but it's time for a reality check I love functional programming as you all know by now but how many people in this room do pure functional programming at their day job and are paid to do so ok great yay go us all six of us the reality is that most Scala programmers don't program functionally that's just a fact and in my opinion it comes down to this following graph that I've made pure functional programming is like a lot of things in category theory and algebra and mathematics and so forth it's very useful for us as developers because it helps us reason about the software that we write it helps us make correct software but this stuff is it's directly beneficial for us but it's only indirectly beneficial to the business because the ROI of us understanding our programs better and being able to stitch things together better and being able to have some idea that they're correct is not easy to measure whereas if you take a lot of other technologies out there and in other paradigms like you know akka and SPARC and so forth and different abstractions that you use in your day to day programming a lot of those convey immediate like actual measurable benefits to the business people use akka because they can stand up distributed clusters that just run with supervision and all that stuff and that solves a business pain right away and don't get me wrong by the way everything that directly benefits you as a developer indirectly benefits the business and everything that directly benefits the business also indirectly benefits you because it's stability or it's good for your career stuff like that's good for your team good for growth in your department but where we really want to be is the top right hand quadrant of Graff where we're doing things that help a developer a reason about their software and build correct code and so forth and and directly benefit them make them a better software developer but also things that directly benefit in the business they have a direct impact on things that matter bottom line things that matter to the business so for today I want to take a step back and let's just pretend we hate functional programming throw away all that type safety and composable stuff and equational reasoning just throw it all in the trash can we're gonna think like a business person and what I want you to do is go through this scenario with me let's say we have we're building some sort of modern fancy microservices architecture and we get these requests you have thousands of requests that come in every second and these requests we have to parse them and then we're gonna we're gonna end up doing a bunch of things we're gonna make sequel queries based on these to the database we're gonna load some some stuff from cache and do more query if we have to if it's not there we're gonna call other micro services API is running inside our infrastructure we're gonna call third-party api's we're gonna do all this stuff and what matters to the business is being able to handle a lot of these every second and also being able to decrease the latency because for most types of business 900 additional milliseconds translates into millions and millions of dollars of lost revenue so in the ideal case what we want to be able to do is handle all these different requests and sort of explore this graph do it all in parallel and then if something goes wrong like let's say one of these sequel queries that was critical to the success of this service fails and we have to return an error back to the the client that initiated the request we're gonna do that and we're gonna shut down all the other computation we're gonna clean up all those resources we're gonna stop reading megabytes or whatever it is from all these different places we're gonna stop that right then and there as soon as we know we're gonna fail and as a result we're going to conserve resources tremendously and we're gonna minimize latency because we're very recive and doing this in parallel to begin with and this type of scenario is super common you probably had to work on something like this and most likely you've had to made trade offs because of the tools that you were using that the business didn't want you to make but they were sort of forced to because you didn't have enough time to solve this in the right way what are the really big pains here well we have a synchronous code and lots of it because we don't want to block threads it's gonna suck up native threads and kill our performance we have to handle thousands of requests per second so of course we're gonna need a synchronicity we're gonna do stuff in parallel to the maximum extent possible one or request could come in and kick off like lots of different threads that you know we use here and there as we're going and satisfying all the business needs of this request and then we can't leak resources ever because if we leak a thread one time well we're gonna leak it again and again and eventually we're gonna bring the production server down not just threads but sockets and all these other things and then we want this thing to be really really fast so it's not constantly falling over and costing us loss of revenue you want all these things and these are business pains they're big meaty business pains and if we solve them then we're in business like we're we make the business happy and ideally we hopefully solve it in a way that makes us happy too so this is what Scala said eight effect is all about what it is is it's a small composable collection of data types and type classes they're gonna help you and me just ordinary developers build principled performant and pragmatic IO applications and these applications aren't gonna leak resources ever they're never going to block and they're gonna scale across multi-core systems and the heart of this effect system is a new data type in a runtime system for this data type which is called IO and in in case you haven't seen anything like this before you can think of IO of a as being a better version of future of a what IO of a is at it essence is nothing more than an immutable value it's just a value it's like Scala list or Scala option or something like that it's nothing more than an immutable value and this value describes an effect FFEL program so a program that interacts with the outside world that either produces an a it's gonna fail with a throwable or maybe it's gonna run forever that's what an IO of a is is that immutable value that describes an effectual program that produces an a throws some sort of error or on time error or just you know spins forever runs forever that's where it is in every single method on IO or an explore some of them today they're all exactly like the methods on lists they're always going to return you a new data structure a new IO I'm never going to mutate anything there are no effects everything you're gonna see today is gonna be purely functional and if you keep it focused in your mind that this is nothing more than an immutable data structure you'll follow along just fine so what does it help us do well ideally it helps us build these very quickly build these asynchronous concurrent and leak-free performant applications which by the way happened to be types a purely functional composable and easy to reason about Shh so let's go in a little tour of scholars at 800 and what I'm gonna do is I'm gonna show you all the building blocks inside this i/o data type so all the primitives you're gonna see them all today and I'm gonna show you more than just the primitives we're actually gonna derive some of the advanced functionality inside this library using these primitives and you're gonna see firsthand what it means for functional programming libraries to be composable when things have the right shape when you separate the concerns and they snap together like Lego blocks you can assemble them to solve tremendously complex problems in a very straightforward way and you're gonna see some of that today so how do we run these things well it's just a data structure what you can do is if you want to run an i/o program you use a runtime system which is baked into this trait called safe app so all you have to do is extend this from your application and then presto you return an IO of unit instead of unit that's the only difference so you're returning a description of your program instead of actually doing the effects and then in this particular example there's a little bit of code that ask you what your name is reads your name and says hello John or whoever good to meet you this is an interactive effect full program and yes it looks a little different probably than what you're used to but it does all the same stuff all you have to do is put up with the fact that it's gonna be a little bit different a little bit more similar to the style of programming that you're used to with future and abstractions like those IO has a function called apply that's capable of taking a pure value and lifting it up into an io of a so if you have a an int you can lift that right up into an io of it it has a function called map just like elections do an option which takes an io of a and turns it into an Iowa of B by allowing you to specify a function that takes an A and turns it into a B so if you have a program that builds an A you can turn that into a program that builds a B by mapping over it of course these programs can be sequential so they support this flat map operation which allows you to describe two programs in sequence where the second one actually depends on the value produced by the first one and this thing is is called flat map and it allows you to build sequential programs that depend on results of prior computations you can produce a program that fails with the fail Combinator just specify a throwable and there you go you get back an IO of of that a where a can be anything it's unfixed and that program rep represents failure there's no exceptions being thrown here this is just values ordinary functions and ordinary values you can recover from a failed program by using the attempt function which takes an io of a and turns that into an io of throwable or a that funny little symbol right there is Scala Zed's disjunction data type and I know what you're thinking why the heck did they use that symbol there they should have used Unicode well if you can go from an i/o of a to an i/o of throwable or a you can also go the other way and this combinators known as absolve and it just submerges the failure into the i/o datatype and we're gonna use this here in a second and this we we derive we can also using just the stuff we've seen today create this or else Combinator which will do this io of a or if it fails it's going to try to do the other one and this allows you to create actions that represent recovery either we're gonna open the primary data file or if that fails we're gonna open the secondary data file and look at how clean and elegant that is me just building data structures open this or else open this and this is derived in terms of the other ones this IO type wouldn't be that useful unless we could interact with the real world and do some input/output so of course you can do that with the sync method which takes some imperative code and imports it into this data structure it describes that effect full code in in terms of one of the terms in the sum type that represents IO and we can use it to build functions that describe interactions with the real world like here I've built put stir line which prints something to the console and reads stir line which read something from the console and of course these functions don't actually do anything other than build a data structure but still they describe that interaction with the real world so that later on it can be interpreted by the runtime system and this this is all that's necessary to build console programs any console program you can possibly imagine if you're gonna express that as an imperative program you can express it with IO and that's true for IO in general there's no program you can build using non functional programming techniques that you cannot build with using functional programming techniques it's just one-to-one this syntax changes a bit but it's same thing so io has a another function which allows you to import asynchronous effects into your code and if you've seen something like this before it's gonna differ a bit Scala's at 8 I actually has a different notion of importing an asynchronous effect one that's strictly more powerful and what this asynchronous effect constructor allows you to do is specify a hunk of synchronous code that's gonna accept a callback and then later on it's gonna call it when whatever was going to happen does Neddie Neddie calls you back with your next chunk of data from them from the byte stream or whatever you're gonna call that callback with the result or if there was an error you could call it with the failure but also this constructor allows you to it it has three terms here the async return your your effect can either say okay I'm going to call that call back later by returning later or it can say you know this really isn't an asynchronous effect at all I was able to compute the value right now maybe it's a cache or something and so you can return it right then and there and that is critical for building highly performant effectual systems because it allows you to say we don't know if this is going to be asynchronous or not we certainly don't want to use callbacks if it's gonna end up being synchronous so we're gonna execute the effect full code and then if it turns out that it's gonna be asynchronous we'll call the callback but if it turns out we could compute it right away we'll just go ahead and return the value there and this allows you to avoid stack overflows and avoid expensive like call acts upon call backs upon callbacks for the very broad class of effects that it's not determinable whether or not they're asynchronous until you're actually doing the effect full code and then finally and this is probably the most important thing it allows you to return and return a promise basically to say ok I'm gonna call your callback at some point later in the future but if for any reason this computation has to be canceled then call this hunk of code here to cancel the effect of that asynchronous computation and what you should be thinking here is I'm okay I'm one effect maybe reading a megabytes worth of data across this socket if we decide you don't need that computation because something else in some other chain failed we don't want to read but rest of the megabytes worth of data or if we schedule something for two hours in the future and then we decide that's not necessary you don't want to have to have that thing run and perform all its effects two hours future into the future so this allows you to when you're importing these raw base effects it allows you to specify a way to cancel an asynchronous computation and to remove the runtime overhead of that asynchronous request and that is extremely important here and it's a missing step in many of the other libraries out there here I give you two examples where I use this to build a spawn which just takes an a and computes it on a different thread by using that async constructor and then another one called never which is never going to call a callback and this is the equivalent of sleeping forever it's just gonna you know run forever it's not really gonna do anything but it's it's a handler that's never gonna call its callback so here here's an example of how we would use this after we after we hopefully we're using FP libraries that do this for you so you don't have to but if you have to you just write a few of these different importers to import the different effects into IO and now you can use them like ordinary synchronous code there's no difference from your perspective and these effects in general they're gonna miss mix synchronous parts with asynchronous parts you don't have to worry about that it will all execute transparently even if you have these mixed effects and also in a performant fashion in this example I do one HTTP request I get back the result and I do another one based on the value of that so I actually do need the ability to chain these computations together and and I do all this without any callbacks there's no blocking of any type here there's absolutely no blocking in Scala at 800 and no reason why you would ever have to write any blocking code at all so you can minimise your use of native threads another primitive that's very useful is sleep so given a duration it's going to return you an action that resumes after that specified duration has elapsed and you could use this like this time to sleep I'm gonna sleep for ten seconds and then I'm gonna wake up of course this is not actually sleeping this is using that async constructor and scheduling this in the background with the ability to cancel it and so forth and and then it's resuming that computation the computation will be resumed at that point in in the future given this we can define a little Combinator called delay that just delays any action by the specified amount of time very simple and we'll use this here to build something even more calm complex so now it's time to talk about concurrency I said that IO included those concurrency primitives inside of it that help you solve real-world problems that matter to the business well what are those there are a couple of different ways that we could have approached this problem first off we could use threads but the bad thing about native Java threads is that's there at the operating system level that consume OS resources and they're very heavyweight and it's not really possible to interrupt them in a safe way that guarantees preservation of resources so doing things like doing you know a whole bunch of stuff and then canceling other stuff just not possible with the threading model we could use green threads like Haskell if only we were doing this at the language level but we don't have that ability we're not gonna write a Scala compiler or plugin to do this he's probably not but green ted's threads do have the ability they're super lightweight so you can have tons of them tens of thousands hundreds of thousands of green threads you know they scale really really well and allow you to build these modern architectures without any paint they're very very efficient but we just don't have that as a resource available to us so the the concurrency model that I chose for scholars at eight is a fiber based concurrency model and a fiber is very much like a green thread except it's implemented in user land this is implemented as a library and they're super lightweight they actually have zero cost for pure FP pure FP imposes its own sorts of costs because you're building these data structures and you're interpreting them to effect later and it turns out that when you build an interpreter if you if you build it in the right way you can do fibers at literally zero costs so there's no costs for us using them and we can define precisely the semantics of these fibers because they live inside userland library we can say yeah okay we're gonna add interruption and we're gonna add timeouts or going to add this or that we can add whatever features we want we can bake it in at very very low cost and these fibers because they don't necessarily consume a single thread they'll of course use threads but fibers will yield to each other under periods of high contention we can scale them way beyond the limitations of native threads we can have tens of thousands hundreds of these thousands of things at a time you know some of them are gonna be suspending through async computations and so on and so forth but we can have huge numbers of these things and they can yield cooperatively to ensure fair use of resources so these are very powerful abstraction I think you're gonna see a lot of the concurrency work in the JVM is gonna move towards fiber based models because they're just too powerful to ignore and then in in any concurrency model you have to choose one or more concurrency primitives the one I chose for scholars at eight is based on fork and joint it's extraordinarily simple I can describe it in a single slide and the semantics are are almost as simple as the type signatures that you see here Fork spawns off an independent fiber so if you have a IO of a and you call for cut on it you're gonna get a fiber of a and that fiber gives you a handle to something that's running elsewhere and there are two things you can do with that handle you can either join it if you want to get its result at some point in the future like you've done a bunch of stuff and now it's time to get its result then you join it which is of course non-blocking or you can interrupt it and interrupting will put an end to that fibers execution and all resources will be totally cleaned up and released safely so you may have constructed a big big long imperative program that's doing all these different things if the result isn't needed for whatever reason the end of that program whether it was going to run for two hours more or two seconds more is gonna be chopped off and all those resources that you would have had to throw down the drain will now be freed up to be used by other fibers executing on the system so those are the two things you can do when you fork a fiber you can also specify an uncaught error handler so you fork a fiber but you want to know if it does something terrible and kills itself and whatnot you can specify an uncaught error error handler which will be invoked with any errors that that fiber issues which it does not catch itself here's an example of how we can do Fibonacci sequence using fork/join it's trivial it's just a couple lines of code we do we do the Fibonacci we call itself recursively on n minus 1 and n minus 2 we fork those off then we join them back together and we add them and this will suck up all your CPUs it doesn't matter how many CPUs there's there exists a n sufficiently large that we can suck up all the CPUs doing this number crunching and it's extraordinarily easy to define them and and of course we don't have to deal with real threads or any of that other messy stuff for leaking resources this is a very functional very declarative way to solve that problem then another one of the primitives baked into scales at 8 is race with which is a much more powerful version of the race or the first completed of that you've seen in other libraries and what Ray Smith does is it has two things and it's gonna erase them it's gonna start them off independently and one of them is gonna finish first and the other ones going to keep going and at that point it's going to invoke a function you passed in called the finisher and that finisher is going to be provided with the the winner whether it was left or right as well as a fiber to the loser so you get the get the value but you also get a handle to the fiber that lost the race and you can do anything you want with that fiber in particular the ordinary race Combinator can be defined by killing the other fiber so you got the winner now interrupt the loser and then boom you save all those all those resources every single other definition of race in any library out there is broken and it's broken because if you call race on a and B it's gonna run them both to completion it doesn't matter which one finishes first you could have hours of computation that you're burning through memory and threads and socket IO and all that stuff and it's all just gonna keep on continuing any any first completed of or or race Combinator and other libraries that they're all totally broken and then you can also with this race with you can actually well here here's the definition of race it's so simple and by the way the type signature of race with tells you so much about what this does the type signature of race given IO of a and IO of a returns in iove' doesn't tell you much there are lots of different ways you can implement that but this one's so polymorphic that there's you know what it's doing just by looking at the type there's strong guarantees here that helped you reason about what this is doing the definition of race is just kill the loser with an error saying you lost the race and given this definition of race we can do a generic a leak-free definition of time out which is amazing because all the other time out combinators out there they're broken too we can take an io of a and we can time it out in a certain amount of time if that time elapses then we're gonna kill it and how do we do this we just sleep for that amount of time and then produce an air and then over on the other hand we run it and then we we call race with and then we we kill the loser and we're done and then we absolve absolve the air so this is a very compact compositional definition of a timeout that doesn't leak any resources ever and then of course with this same definition of race width we can do this par Combinator and this par Combinator takes an io of a and io of B and runs them both in parallel I'm and it does it just there's a few different cases you have to contend with but this is good like these cases actually force you to deal with I'm running this one and then this one fails what should happen well we don't waste computation and let this and keep running so we we chop it we interrupt it and then vice versa t it's symmetrical and so forth and then it's only in case that both run to completion that par of a and B actually produces a tuple of the results retry once we've built all these things I mean we could just add stuff all day long it's just so incredibly easy retry it's it's this action or else are you try and that's gonna retry forever until the first success or we can retry in if we want to retry up to end times very simple we can retry for a specific duration so we can retry for up to five seconds there's no there's no messier it's just very clean very simple elegant compositional definitions that use the primitives that we've already built and assemble them in precise ways and all these Combinator's because they're built on these primitives they have the expected semantics so this infinite retry action of course it can be interrupted and when it's interrupted well all that stuff like that comes after is it's gone it's not sucking up memory and CPU and and other things so the fiber concurrency model requires you to have a way to communicate between fibers one produces a result and you want to do something over here an M bar is a standard concurrency primitive for doing that in haskell and i basically copied the haskell one over i mean not the implementation of course but the type signature and this m bar is very low-level it does everything synchronously whenever possible it will never spawn threads just to spawn threads or use actors or any of that stuff this is super low-level very hand optimized very efficient and it allows you to build other things like channels and concurrent queues and all sorts of things can be built on top of this and you can rely on it because it is very well optimized it's very very and takes advantage of the much more powerful async constructor that we have in this IO data type that allows you to determine whether or not to do an asynchronous action based on executing some effect and here's an example where we create an empty M bar and n bar by the way is a zero or one element blocking queue doesn't actually block installs at a it's totally asynchronous but you can think of it as zero one element blocking cue so create an empty M bar and then from another fiber which we're just gonna fork off we're gonna put something into the M bar and then from the original fiber we're gonna take and what happens is this this line here this line here actually suspends until this one runs and put something in the M bar so that there's something to take and I actually don't think this is quite enough I think scholars a Tate effect needs one more thing and that's a real software transactional memory implementation that is patterned off of the one in Haskell so a real STM that's cleanly integrated into IO and using these two things you can basically solve all the all the concurrent state problems you can imagine in a very simple declarative way with a very high performance so resource safely just real briefly we're going to rush through this sometimes you're unwanted to inaction and you're not going to want it to be interrupted there will be some critical section I would encourage you not to do this because remember someone can always pull the power plug on your computer in which case your uninterruptible action is interrupted so you have to design with that in mind but nonetheless if you need a critical section where you don't want to deal with the possibility of interruption you just wrap it in uninterruptedly and you're guaranteed that that fiber will not be interrupted as it executes sin that critical section and this is a compositional definition so you can nest these inside here and it has the expected semantics Brackett is the primitive that you need to use whenever you acquire a resource because Brackett provides a guarantee that anything you acquire will be released regardless of all the things that can go wrong so if you open a file you want to call bracket and and specify closed file in there as your action for releasing the resource and now inside the bracket you can use the file to your heart's content and you know that no matter what happens if this fiber is interrupted or if it terminates for some internal reason it didn't catch some air that it threw inside then on the way out that closed file thing is going to be called assuming that the acquisition of the resource succeeded and both the action that opens the resource in the action the closes the resource have the guarantee they will not be interrupted the body can still be interrupted unless you surround it by uninterruptible um so the body could take a long time in general you want it to be interrupted but acquire and release cannot be interrupted and you have the strong guarantee that if a choir succeeds release will be invoked and this happens regardless of how deeply you have nested brackets gonna have brackets inside of brackets and these guarantees survive with bracket you can make the equivalent of finally and in it's as simple as this you bracket unit basically and you call the finalizar in your release and then you do that this action as your body and then you have a nice way to get all the same things you would get out of finally only in a way that's not broken this is the Scala model for finally does anyone know what this prints can you guess no guesses so you no one even knows what this does but there's three exceptions thrown here so you can imagine that two of them are going to be swallowed for sure well scholars at eight never swallows any exceptions and it does the correct thing here as opposed to Scala which in my opinion does the incorrect thing here and this is what it looks like in Scala is at eight all the heirs are reported two of them are reported to the uncaught air handler for the fiber those that were thrown from the final E's and then the one that was thrown from the main body not know finally that's that's the run time failure that's reported at the top level so here's another very powerful and the last one we're going to cover supervision so you can take an IO of a and you can supervise it and that means it's gonna be forking stuff like crazy left and right when that IO of a terminates when it reaches the end everything it forked will be cleaned up safely all those fibers will be shut down in a clean fashion this means no more thread leaks and you can use this in combination with Fork if you want to fork some computation out there you can make sure all of its allocated resources all the fibers it spawns out our shutdown when it terminates and and cleaned up with zero loss of any resources including threads and it's as easy to use as this you just wrap it in the stuff you want to supervise and at the end of that block it's gonna terminate all the things that were forked off all the ones which are not currently terminated so these things actually have algebraic laws there are ways we can describe these things for example we can define if we can describe the relationship between fork and join by saying if we fork something and then we immediately join it that computation can be replaced by the same thing without changing what that computes so these these are more than just operational laws they're algebraic laws that give you very powerful reasoning across the classes of primitives that are capable of these types of things all right so real quick now just two minutes spend two minutes running through this performance so what does this look like well Scala is at 800 is very fast in fact it's as far as I know it's the fastest purely functional effect Monad out there by far in some cases it's more than six thousand three hundred times faster than competitors and this is just the result of sort of optimization looking at bytecode and minimizing heap allocations and and so on and so forth it's nothing revolutionary you can see the code here in a few minutes but I included future here even though it's not a functional effect monad it's a non functional pseudo monad but I included it here for reference because you're probably using future so future is really really slow compared to scholars at 800 its calls that a IO has all the same stuff in its future only four more with fork stronger guarantees you know now have a business case to go to your boss and say look it's just not pragmatic to use future we got to rip all this stuff out and use this purely functional stuff known as Scala's at a tile you you have that like a business reason to go there and to use something that's up to 200 times faster with much stronger guarantees that are gonna matter to the business in terms of safety scales at a do is very very safe and doesn't have any implicit executors or mutable and implicit sit has that bracket primitive it has guaranteed a sink and sink stack safety even in pathological scenarios this is a very safe primitive to use and it's very expressive it has it doesn't have a ton of stuff in there I mean it has lots of the Combinator's that I built on top of the core primitives this has a few core primitives but these have the right shape they're orthogonal and they can be snapped together to solve all the problems that we encounter in today today program so a lot of people are gonna say well what about FS - why not just stuff FS - on top of an i/o monad to get some of these guarantees and my answer is no because an IO monad is not a stream and IO is approximate of one value and a stream is a production of 0 - many values you should never use a stream to model something that you would use an i/o computation to do for and I actually think that FS - it's a really good streaming very but nonetheless it's a sign that we have some work to do in the scholar community fs2 does not have there does not exist inside the scala ecosystem the primitives the foundations necessary to build software like fs2 so it had to build a lot of these things itself and that's sort of reflected in look at the source code it has a mini actor library a mini FRP library it has a m bar implementation it has concurrency primitives and some of these are actually leaky it had to build all this stuff because it didn't exist inside the scala ecosystem which is a problem we need to fix this definition of race here for example is broken if you raise a hundred things and and the longest one takes an hour to complete and the shortest one takes 100 milliseconds guess how long that thing will be running for an hour and all those things all the loos are all ninety-nine losers are going to be running and doing their effects sucking up memory and threats and this is nowhere more apparent than then in this time to get method which is in side ref so ref is is FS to s implementation of m bar and it has a time to get why does it have a time get well it has a time get because you can't layer in timing out with the act of getting given this definition of an effect monad doesn't have the right shape to do that in orthogonal way inside of scholars at eight a timeout is as simple as doing dot time out on your IO action and it's not just to get that that applies to but it's the put it's whatever you want this is truly compositional and it allows you to layer in the concern of timing out separately from the concern of actually doing these actions because these pieces have the right shape they can be snapped together to solve your problems all right so this here is for the six people who raise their hand at the beginning okay this is a call to action I want to say here that five years from now I want to be able to ask that same question and have every single person raise their hand and say yes we are all doing pure functional programming but the problem is like I can't I can't they're like not by myself and you can't get there like it's up to us as a community to go out there and to solve the real tough problems that the business needs solving but to solve them in a way that allows people to reason about their software and it's going to take all of us quite frankly these are hard prompts like taking on some of the software out there it's gonna require all of us working together as one figuring out how we can show that functional programming does more than just help us reason about the software it helps solve those business problems so thank you to special thanks to Alexi and to Twitter for hosting this talk and for all of you people for coming to my talk that's it I am going to do the pull request now because I said I would but I'm totally out of time and they're about ready to pull me off stage so thank you very much I like him to stay up here to do the full request so you can watch it watch just beat sir so head out there grab some lunch and started excellent thank you yes thank you for coming it is it is applicative it's a it's a semi it's a semi-group functor right yay all right here we go ready fingers crossed there we go thank you