sfscala.org: Ryan Delucci, Building a Reasonable Expression DSL with Kleisli
Recording: sfscala.org: Ryan Delucci, Building a Reasonable Expression DSL with Kleisli
hello everyone yeah thanks for showing up and coming to see the talk and I also want to give a shout out to the folks organizing for SF Scala oh sorry for folks organizing at SF Scala thanks as well and also to chartboost so today we're going to be talking about a technique for for building expression dsls using using a number of category theory concepts with with Kleiss li sort of being the star of the show so let's I guess such a problem it's probably give like in a quick introduction myself where so I I actually I'm working for Verizon on the on Q team right now working on infrastructure services for the video platform that we have there and and we're we do a lot of we do a lot of work using a pure pure functional programming principles which is which which I'll be getting into honest honest talk so so what we're actually looking and doing is we're talking about building a an expression DSL such that you have you have any type of input on any kind of domain and you could actually you you have you have your expressions compose and and it can actually compute results based off of them and and it's actually it's it's a by reasonable I actually I mean that in the with all the annika all the connotations attached to that in fact where you have you have trance you have a referential transparency where you could actually wherever you have the expression at any place within your code it's always going to generate the same results and you won't have any side effects so that's that's really that's really context of what we're doing here so let's say we we want to actually compose in expression and this is sort of a notational embellishment right here I'm not I'm not saying that the adapter is taking X's input now putting X that's just an expression saying okay we have some adapter that's returning this value so we have this rhetoric shostakovich or this returning five and we're comparing the length of that adding them together and comparing it against 47 and by adapters I mean any type of data source where data is actually being pulled from it could be from a database it could be from a pis and the idea being that you could actually have errors be generating from these sources so we want a way to build expressions so that as you as you as you as you apply them to different sources or data is coming from you you could actually handle all the the error reporting so for example you have an expression like this where kaboom is returned as snap is reach or less at 47 you would have you would have all your your your error conditions returned as a result or you can have expressions where errors might be generated by the expressions themselves like in this case you have a divide by 0 so so we're going to be we're going to be building and an expression library using that that supports different types of operations unary binary operations we're going to have our error handling completely separated from the core logic that that's a key thing and once again we're being completely agnostic from where the data is originated from hence the adapter the adapter notation as I was showing before so we have to handle these air conditions and and yeah so that's that's what we're after now we'll be using tools of Kleiss Li or the tools of category theory of Kleiss li being one of them and we're gonna be talking about how you would build this library of operations and actually compose operations together to build these expressions then here is a sort of like a sort of notational way you can think about where you have values from the different adapters being gathered together and you have a result that's computed so here's sort of an example expression DSL that we're going to be putting together tonight now as you can see here we have we have an expression composed of symbols where we say X to the N plus y to the N the triple compare cuz we're gonna be defining that as part of our DSL is equal to Z to the N and n is greater than 2 you pass this map into it and then the results computed now does anybody want to hazard a guess about what what's going to be returned here this can always be the same value this could be a false always no matter what input you have here because this is fair Moz last theorem or it's a it's a according to fair mods last theorem there there will never be a case where that will hold so that's just that's just example for this so so let's let's take a step back and look at higher-order functions in a general sense so we have probably the simplest form of a higher-order function a perform where you pass in a function you apply it to a value you have we have two two very simple functions here we have one that bumps a value up by one and one that splits it in half and you can compose them together where you say okay well we're going to split it first and take that result and then bump it so you get a four or sorry you get a three from the value 4 and which means the same thing as this it's just that's why it's a lot of more folks will use and then because it reads a little bit more more smoothly so this is how you would compose functions you know in a very straightforward functional sense however there is there's one thing missing there is that is all your values that are computed there there there's no particular rules that can be that can be applied to them there it's pretty much you know you have you have your input type and your output type and there's no real laws or any patterns that apply to that all it's just these are just just straight-up functions well we're gonna actually look at the definition of a monad and and you'll you'll see how this fits together in a moment so so I want to just just show right here I mean if you were to open up skull it's called Z's definition for a monad you would you'd see a lot of other methods in there it turns out that these are really the ones that the ones that you need to define a monad and just I'm going to do a quick run-through of you know what what those methods mean so you first have point the point operation and all that does is lifts a single value into the form of a monad so you have like an option where you say some five okay that lifts at five into the form of a monad and then for a sequence you could actually you you lift a five in the sequence in the arm now sequence of one element and in these cases there are they're empty cases where you get at the base case would be none for option int and for sequence and it would be an empty empty sequence and right so that that that's a point operation and now there's bind which actually is known as Scala's flat map and what that does is it transforms values from type A to B while at the same time lifting them and and combining them into a single single collection and you have an example this right here where you say okay we have some value and we do a flat map on on on the for the value and inside of it and what happens if you don't you don't end up with the sum of the sum of the length you end up just as sum of the length at the end was sum of 12 and the word same works for a sequence so that's really kind of the idea of monads it's about it's about consistency creating patterns for how you how you contain data and how you in how you compose data together and this this could be really argued as as the why the named you know pretty much the building blocks for pure functional programming now I'm just going to do a quick comparison of what happens if you what's what's a world where you have monads and when you and when you and where you don't so say you have like example here you define a string is a option of string a am with Jenna and you have a and then we have say the same same thing down here be and B of M or B M well if you're if you're if you're watching map map it to the length using using a using the monad form it's it's that simple whereas here if you have to if you don't if you're not using Moe Nancy check the snow so you have this conditional error it doesn't read as read as easily and as you come as you deal with more more pieces of data trying to compose them together it's really simple here just a flat map if you're talking about combining two here you have you know these this four different paths because they all to Mona Mona has being used there so this is a very simple example and you can see right away that it's it's that that using monads as a as a as a principal it actually allows you to allows you to compose different pieces of your code together using in in uniform ways and that also involves I mean you have the different mode Monad laws that that apply there and you know involves identity and you know then I it's a little bit out out I I'm not going to be getting too deep into the theory of the category theory of laws right here but but that's that's a part of a that's how you get the consistency for composing different objects so let's say you have a function right here where you for a to M of B well this it turns out that this when you actually lift this this actually is what a Kleiss Li is and it's literally a function where you're mapping it's it's it's a it's where you're mapping from an a to a monad of some to some type B and that's that that's all Kleiss Li is you conceive as a function that has a that has a that's you know that's been sort of blessed is a is a monadic datatype now I just since I keep talking about Kleiss a I figured I'd put this slide up here so folks that know what Kleiss is yes sorry to do like the different composition operations you have to you'll you'll find that you'll need you'll need to have the monad type like within scope like you doing like you're composing different monads together or different Kleiss Lee's together I should say so you'll find that there's some there's some methods in the scalzi where you actually need to have it defined as a monad and also an example of defining one later on yes oh sorry yes so he was asking if this needs if this needs to be a Mon if M needs to be a monad in order for for this to work and you can actually define you you could define some places where it's not a monad but you'll find for certain operations if there's no implicit monad monadic type for for it in scope you'll find a certain operations you know you'll see a compiler error saying okay well there's that there's no it's not in scope you can't you know you have to define one of these so so the answer is yeah if you want to do all the different composition operations you need to have one defined in scope so and for the example DSL that we're constructing I'll show an example Mona a definite monadic definition and so yeah so once again this is a so this Kleiss Lee wasn't DAC he didn't actually invent kleiss Lee's himself but he's done work that's related to type theory and he actually so so cly sees are actually named after him so I just had to throw this site up here so now we're going to build the expression DSL now here is for for our language that we're constructing we have this notion of a term and a term think of term is in terminal value so it can be it's things like either the input into a 2 into an expression or the output alright and it could it could be an error condition doesn't necessarily have to be so we're we're defining right here that so this is a definition of a term right here you'll notice that we have this diamond operator that I'm defining in different places this this comes in handy later on for expressing binary operations so you would have like say a term of a diamond F that that is a a term of a binary operation where those are the left and right operands and so so very very simple definition just contains a value and the term monad is here's how you would define a monad word to find the two point in buying operations we're saying okay we want to lift a value into a monad you just simply create a new term and set the value to you know what you're actually lifting into you know the value lifting into the monads that's the point operation very simple definition and then we have the bind operation which actually it takes in a term a and then it takes a function that goes from a to term of B and once you have those two well okay we just need to take we take F and the function from a determine B and we just take the value out of term a and then just pass at the F that's it that's bind so once you define both of these you it's it's now it's now a monadic datatype and you can you know do maps flat maps and and such I should say strictly speaking map map is actually a requirement for for functors which is a which is a super which is a monad is a type of functor so yeah I'll try I'll try to inject category theory here in in doses where it where it applies so we have so we have a few term definitions there's an integer in the string so the very you could define bullying at the fight complex numbers you can sorry sorry I had a question for the previous slide all right yep is there a separate definition for the greater than and less than as a knight do you mean greater or less than Oh for it for this right here you're saying so this will right here yeah so there there's actually yeah elsewhere I've defined a case class right so I've defined a case class that represents the left and right operands honestly I can't remember if that's in the slides or not but yeah so I've defined a type that the diamond type that serve contains the the operands for binary operation yeah so so yeah so we get to find any any types of terms that you want to use here now let's say right actually here it is okay so this is this is the definition this is a definition right here so you could literally pass a tuple now I could have just expressed this as a tupple but for as you see later on in the code it would get really a little bit a little bit hairy to have that they have the tuples everywhere the diamond notation actually does help a lot this microphone is kind of it seems like certain angle it's louder I don't know so here is an example of a Kleiss lis so we have we're actually adding some capability here to a Kleiss lis operation right here where term is a monad and where we start it's a it's a function going from A to B and what we want to do this is pretty much Express is what we want to do here we want to say okay we have a Kleiss lis a from M of B on the left hand and class II of a and be on the right hand if we want to combine them and make that a Kleiss leave a 2m a B diamond B so this is how you would do that it's actually quite straightforward I mean you're just defining you're classy like this you say okay well my output that I want is a classy from A to B diamond B and the input is on left right just as it shows up there from A to B A to B and we compose it as your say okay you're lifting a function and do Kleiss lis so your input into function would be type a and we're still going to apply we're going to apply a to the left side and the right side and diamond them together like that it's that simple so it's as you might as you might guess composing one of these things so now we're going to build a a library of our operations here and I have a few type definitions just a sort of just sort of as a shorthand for them so we have like say an operation that with no faults and by those I mean that okay this operation does not return any does not generate any errors but it may there may be errors coming from different sources so well we'll get more more on that later but operation with no false would be defined as a term with in some input in some output an operation with faults would be a term input and a and a a fault error condition or the output so this is this is the disjunction this is actually a Scalzi disjunction type which is a it's a way it's similar to the Scala either but it's right bias so meaning if you were to do like a map or a flat map on the Scalzi disjunction you would actually say okay but it would it would apply to the right-hand side so if you actually had an air condition and you did a map on it it would just pass right foods think of as like an option if you were to do map on option it's like like none think of it that way so that's so this would be an operation with faults has some air condition now we have a binary operate this'll be like okay a term and once again we have a boolean diamond boolean and then boolean as a result so so those are two different type definitions and defying these things is pretty straightforward you're just saying okay to find not L I'm just you know to find the classy they're boolean term is actually that's an object that's to find their I don't think there's a slides where from this will actually create the term from the boolean from the boolean value that that's all that is and then map not else you just may not the value so yeah that's not and and is is like this so I'm defining your library functions is really a simple matter of just you know just listing them out and there's some neat things we can do like you have a compose K right here where you have like say a function where it's like okay here's add it just adds them together okay subtract is simply is simply an ADD but you're naked on the right hand side so I'm just doing a classy composition of add in this function right here that's a compose chaos so pretty straightforward divisions a little is different because this is an operation with false this operation may actually generate an error condition and right here we're saying okay well if it's 0 on the right-hand side of the input we're going to say by the way this - before vs. - after these are these are disjunction definitions where you're saying okay it's this little on the left side and this is on the right side so all these symbols seem seem kind of kind of odd at first but as you start using them you it makes it's a sort they're they're kind of they're actually quite visual so you get used to them and after a while you don't really they don't seem awkward anymore and actually a lot better than just writing out like a long name that you know that you might see like you know other other languages or other libraries so so this is how you define division right here and in here is an exponential function right there so building your library is it's really quite simple and you know this once again we're not being concerned at all about errors coming from data sources we just build a library to our we just we can just add different operations to our library as we need them now I want to give a quick sort of a quick sigh side sort of a sidebar here you could actually use Kleiss lis in place of a dependency injection and I would say a good example of that would be to titillate take a look at the implementation of the the the funnel project that we have is open source for under on cue github open source projects or sorry open source library that we have and we we actually make we make a lot of use of cly sleaze for this purpose and you can think of it as okay you have a closely to find and you can actually inject your functionality here and it's it's surprisingly effective and a lot a lot lot cleaner and more more more approachable than using like say like the cake pattern for example so so now I'm going to show how we handle error can handle error conditions and this is actually quite this is quite interesting because what we want to do is we have our library full of operations that do not that zoom that there's no errors coming in they may generate some errors of operations but none of them receive errors well we need to we basically what we want to do is that was for building our expressions we want to take the different operations that were that we're composing together to form these expressions and sort of wrap some wrap some air handling capability around them separately from the operations so we can fit them together and have an expression where if there's an error at the very beginning at sort of the the leaves of your expression then what will happen is it'll actually they'll be routed through to the top of the expression you have that is your end result so you'd sort circuit evaluation of different operations they've just you'd have all your ears gathered at the end of the evaluation of the expression so this is and this is how we achieve that we have survey unary through operator which is for unary operations and what we're saying is okay we have a library operation that goes from A to B but you know what we may have errors coming in to it and we have to allow errors due to be coming out of the operator so in you'll you'll see why I use these symbols later they they make sense as I as I'm putting them together but for a unary operation here's a light the library operations can be passed in here it's going from A to B and the the output is going to be is actually going to be it's actually uh it's actually going to be of this of this type right here where once again yeah it's at the terms of monad you have errors or a value or errors or a value and this is this actually how you you do that and it's it's really quite straightforward all you're all you're doing is as you have your input coming into the function you convert the disjunction to an option and if there's a value there it's not an air condition then you're actually going to say ok this is a value that we could pass to our expression which is our library function right here and we can get the result for that if we have the result we map it say ok it's a successful value but if we have an air condition this this yielded nothing then we're going to go on the else side and we have to take to create the disjunction left sides and it's an air condition and the way to do that is we have to take a was a disjunction and swap it in order to get at the the error and actually return that so that's that's why I say a swap head because in order to refer to the error you have to you know it's right by it so you can only get to get the right side of this thing so we have to swap it swing get the error side of it and return as an air condition and that's that's that's it right there for handling for unary operators for for by any operators it's very very similar the difference here is now you have you have the case where you have on both the left left and right hand side you have to deal with so that's why it's the same idea here where you're actually getting you're converting the disjunctions to options and only if you have both of them you can pass them to the to the operation right here and then you have the same same sort of thing here this is a bit more complicated because you have once again we have I mean it's a binary operation and we're saying okay we're mapping if it's successful we have to do a left map on this this means you're lefting you're mapping the left hand side of the airside and you're mapping it to a non empty list this is really only need to be cut there to have that the types lined up correctly generally when you're doing a map like that when you do a map like this on the right side means that have successful so that's really just sort of a further or at I plant the the you know once again the types correctly there and then you do a get or else and you just simply this combines all the errors together we're doing a swap again on this C and we combine errors and reduce and append them together now by the way I I forgot to mention the non empty list this is a nice feature built in the scalzi that represents a list as has it sound it's a list that is guaranteed to have at least one value in it and this is very this is very helpful spent specifically for air conditions I'm sure many of you have seen where you have like some API some annoying API return they say oh there's there's errors here and then you look at the list of errors and it's empty so then what does that mean that's the ambiguous it doesn't you know maybe there wasn't errors I just don't know what they were or there was no errors I mean you don't know with this you could actually guarantee have the type system guarantee that you have a list with at least one one element inside of it so it's actually used a lot for for error conditions so okay so now we've handled errors we can let's let's put two let's have a nice little representation of an expression here where we okay the expression from input the output is really the same thing as an expression input two non-empty list of false or the output so I have this nice type definition or a trade I should say that defines okay here's an expression and it has a closely inside of it you could pass past a value into it because remember it's classy as a function so you get evaluate it like this and then you have you could define your expression there so that's kind of a handy you'll see that use later on yes so this is uh oh okay right yeah I forgot so I forgot to mention there's one other thing that's different about this binary version is that the unary version assumes that's why I named the symbols the way they're named is that I'm saying okay this is for operations that may not generate any that will not generate errors okay this is for operations that may generate errors that's why this is there is that your question on this slide right here okay why do I have what again sorry yeah if your input oh okay this this is assuming right so this is yeah this is what it used was in a context where you don't have you don't have failures on the input yeah but I I actually that's a good point to see what we're getting out that the the context where I've used this is where it's it's an expression that doesn't that you have like a fixed input value already so it's usually used later on in the chain where you've already gathered the values from the sources but yeah I see that makes sense so so so yeah that's that in this expression you'll see it use later on so so now we're gonna look at composing these things there's a number of composition operators I'm just showing like an excerpt this is from the scholar Z source by the way there's a few operations here there's an and then and compose and you could you could just by looking at the type signatures you can see what's going on I mean literally you're going from a an A to M of B and we say okay we have an art closely of M of you see so your result is going to be closely of M from A to C so you're composing those together that's what this is and then this order does the reverse of that and actually this this answers your question that you're asking before why why do we need to have like this monad defined it's actually a bind that you need to be defined which is a super super type of monad and that implicit has to be in scope in order for this operation to work so you could just define bind and then and then you would you would have you would have what you need which is just the buying method so so yeah so this is so these these are be some operations for composing composing cly sleaze together and we're gonna use this right here let's say we have an integer expression it's a so we take we have a we define it takes us an input of Kleiss Li up here now we have different operations so we're defining our DSL syntax is what we're doing right here we're saying okay we have to find the different operations and from this you could actually you can see how the through operators sort of fit into this picture you take a library operation you pass it through the through the through unary and that that adds the air-handling around the operation and then you compose it with K same thing same thing here and of course we're using the other composition right there and you notice that we're using different versions of this like this one right here this is for division which may generate errors that's why we're using the other version of this and then here's the one for for binary operation that doesn't generate errors but they you know could receive some from the source so these are different operations that you can compose so we're now building the syntax for our DSL here so we're actually quite close now we have we have all the plumbing that we need for the air handling we have we have a way of defining these library operations and composing them together but we don't have any real means mapping our actual language to these concepts we have these symbol definitions here I mean what what are these symbols mean how so they're just kind of sub pieces that we actually have left to implement yes so he's asking he's asking if this could be you if we use applicatives here and particular you mean validations right which is a type of plick ative and the answer is yes and I'm actually going to show a version of this using applicatives and so you can sort of compare and see the difference but yeah exactly you could use applicative as a place of disjunctions but uh but yeah I mean this so continuing with the construction using disjunctions we are almost there but we're missing we're actually missing a few pieces we have to have some way of exciting where we're getting our input from we have to have some sort of adapters as I'm referring to them we have to we have to consider you know okay how are we how are we handling these errors there at sort of at the at the edge of edge of the world of our you know purely functional system so this is sort of the interface with the outside world that we have to implement now and this would be now for our simple DSL we're using a map as input I mean reality you'll probably you probably have like a database connection you know like API or something but we have just a simple map here now it's a map from symbol to any hence I know it's probably it's I mean it's any that that's you know it's any type so it's not or any value so it's really not um the most type safe thing in the world where we're at the edge of the world where we're getting values from somewhere we not don't know what the type is so right here you would define you could define and say okay we're taking a symbol and using the symbol we're pulling the value out of the maps that's all this is right here andreat and returning them as specific types casting them to different types so if there's an actual air condition it'll be the the air condition will be actually trapped right here and this is this is actually a the scalzi try-catch block is what this is this is a much nicer much nicer than the one that's built in the Scala in that what you can do here is you can actually say okay you could pass a you could do something do something here and here we're just casting to type T and you could actually it will actually return a disjunction which you can actually you can you can handle in any any other destruction right here we're doing a left map saying we're mapping airside and converting the exception to a message so a really really elegant way to handle air conditions and that's that's exactly what this does and all these return either air condition left side or the actual value so that's our adapter pretty straightforward we need to have a way of we we need some actually some implicit definitions so that okay we could take a symbol and and in our language say okay this is integer this is a double this is a string because you know edge a world once again we don't we don't have actual type of base on that so this is sort of at point where where where types come into play and it's really as simple as you're defining you define your classes like this it's using the adapter functions that were on the previous slide as int as double a string so it's using that adapter code this is a sort of a convenience right here so that when you're actually passing your input into your expression you don't have to wrap it yourself with the adapter it'll you know actually just say okay you got a map of that well that's really the adapter with the input in it and then here this is for constants so you want a constant value you don't have to do anything special there you'd like to have a converted to an expression for you and that's what this does so now this will actually work after from all the code that I showed this little dis will actually work and you can actually you can have any kind of expression that you want here any kind of input err hamp our errors are handled as you would expect them to now we're going to go back into look at how we could make some changes and use applicatives and the definition now idea here is that well may I should first explain what I mean by you know by applicatives are specifically validations so a validation is like unlike a disjunction you could actually gather multiple errors together and that's that's what the collective's are balanced about combining multiple values and sort of doing doing a handling data in parallel that's kind of the way to think of how what an applicative is and a validation is a type of applicative in that it's the notation is that where you have okay on the left side this your faults on the right side is your value now validation any L means non-empty lists so I could have said validation non-empty list fault a but since it's very common that people will will use validations with non-empty lists that's why the validation nal exists so now we're redefining we're to redefining some stuffs I've defined before you'll find this will be actually a is actually more brief because using applicatives using validations actually requires less code you we're going to find a term as a validation of false or a we're gonna say an expression A to B as a kleiss liter me to be we're going to define a diamond now just simply as a type that's really the same thing as a tupple we're going to say a binary operation is is this form right here now notice that this means that the errors are still in the values here I sort of I let this kind of a sort and exercise to the viewer okay how what could be done here to lift these also into into them in the moment types oh that's a good one to think about for because right now these are actually defined as part of the as part of the values in there but this is how we're defining here for a binary operation and where you lift a binary operation by saying okay you pass in a function from term a diamond terminer to turn B into a Kleiss Li term term a diamond term B and then you know pass a function to that so that's that's what this does and the definitions so we're doing things a little differently we're saying okay an adapter has a Hassim here's an air condition of fuel doesn't exist we're saying our source is a map our keys a string we have a reader for a string a reader for an integer and you see it's really straightforward code there you're just saying okay you have some value coming in here convert to a string and say success in the L and these are these are explicit set are defined fine for you in scalzi and that battle returned a success validation non-empty list it's so that's a validation analog to the disjunction minus V okay its success or sorry V - are you going backwards that's a success entry derp now takes is it's going to parse the integer but if that fails oh by the way parse int that's another nice thing built in the scalzi that's that's actually what that'll do is they'll try to do that it'll try to do the parsed if it fails instead of just blowing up with an exception it'll return a disjunction which you're gonna handle here and you could say okay convert doubts and expect unexpected type and we're gonna wrap it into a non-empty list it's a really really short code here starting to use applicatives and you know sort of dig dig a bit deeper into the scholar Z toolset and now we're gonna have a read here where we say okay we're going to read some key value from this map and that's literally okay got the source to read the key from it and we're gonna map it to map it to this distance reader right here so it's just going to be like however that's basically our depending of it's gonna be integer or string you can have more types here and then if if it's not able to retrieve it that okay field doesn't exist and maps it to that and that's a failure any oh okay so does this make sense folks okay and now we're gonna find our library it's which is actually it's in some ways it's more complicated than the other library but in some ways it's actually a little bit more elegant we're doing I'll show this part and show that in a moment well first I'll say okay you have your trait numeric operations add minus and division and the reason why I have to type parameters here is because I'm saying okay for add in - it's the same you have you add two in so you get it back I'm I'm just gluing the possibility of memory overflows where you have like integer that's so big that's almost gonna I'm excluding that those cases were sort of simplicity here but for division you're you know you're gonna end up with you know yeah you have two integers you know you represent the result as a double so that's why you have like the D the B use right here so that explains that and we're gonna find integer operations and say okay add is where you lift this binary operation and here's the there's lots of clever names for this guy I've heard some folks I I know someone refers it to Cinnabon operator I've heard people refer to it as a TIE fighter operator I don't know if anybody has like a clever name for it I mean there's all kinds Dave's a good thing it was like a plate with like a knife and fork on both sides out all kinds of a but all that does is this is a you have a function that takes in left and right values and this is applicable II it actually combines those and/or actually I should say it splits these apart and it says okay we're gonna add these two together right here so so for addition and subtraction are defined like that division it's different if that one's interesting because it could return an air condition that's why you define this little handy closed division function right here where it takes a division the actual division happens there and you're passing n l and r here but what it's doing up here is it's actually did the tupple this is a built in the scalzi where you're actually combining both the left and right and from here you actually form a validation here okay and that goes in T and then you go here and you're gonna do a match right here and then what happens there is you say ok this is I'm basically pulling apart that's right right here you're actually not expressing anything in form of validations or non validations you're referring to actual raw values but we need to pull it apart and actually refer to we mainly actually have a failure or success here so that's what why this this function exists here and this is actually how you do that and this will return so return the validation as a result here this gets lifted up into a binary operation and you ever close division and that's actually where it you have right here so is that does that make sense all right so as you see that's that's simpler and we're actually pretty close to the end of this definition using a using skull validations you have you define your your your syntax which is you know the actual operators in the language and it's just literally you're defining Kleiss Li's here you're using different operations - division applying it to both contain saying okay you have a left and right and you're playing the source to each of those as functions and then that that that way it actually could compute and actually errors are combined or actually we're allowed we're using validations to do the heavy lifting of combining all the error messages or air conditions as there are any if there aren't any adjustment this is and this is all this is all done because you know we're once again we're using the validation is actually the Monad within the within the Kleiss li because that's that's what term is a term is actually a validation so you can see the finding the operations here is actually pretty pretty straightforward right here is if you want to take the symbols themselves and actually and actually read them you would you know this is using a read function which I define the earlier slide right and that's actually that's actually all the definition right there so so yeah some great resources I recommend taking a look at for learning more about Scalzi and also the cats library of these these are be some great resources to look at and so the cats library is is actually it's a it's sort of like it's it's you think was like scalzi reimagined it's a it's a fresh fresh take on the fresh take on implementing building sort of category theory library so cats means category 3 for Scala so it's it's it's intended to be more and more approachable it it doesn't have all the features that you'll find the scale of Z but the idea is to try to put the things out these essentials together and serve a more more elegant way that's kind of the idea behind cats we actually have at Verizon we actually have committers for cats and and scalzi as well and I I definitely I highly recommend giving cats a serious look I mean it's it's reaching a point there's a lot of it's that the test coverage is really impressive I want to say 70 percent or maybe an 80 I don't remember exactly but it's it's got a really impressive test coverage and and there's like you know coverage for making sure that the laws are enforced for all the different all the different monads and placated Xin shush yes sorry could you have written all of this in cats um I believe so the I believe so yeah there might be some pieces I'd be a bit different I mean I honestly haven't used cats yet I I would say very likely yeah in fact there's some features built into cats that would have been really useful here like where I where I'm dealing with the different the different let's see the different datatypes right have like integers and uh where is that slide right right here I have integer operations and I mean if you wanted to find double operations you have to define like another one these were double cats has like an algebra package that really has some really neat stuff that actually would help unify some of that that's probably one of the next things I'll actually look at sort of like is another iteration on this code so so yeah there's a lot of there's a lot in cats I recommend take a look at I mean a few I mean scalzi obviously has more of your history so there's a lot of libraries that are using that so switching is you know it's a bit of a transition but I see if you're starting with a fresh new project coming cats is definitely worth a look for sure I mean there's a lot of I mean Kleiss Lee's been added to it recently the one of the missions or the things that you won't get with cats is Scalzi tasks and that's that's something that's really that's really handy and we actually use that a lot allotted Verizon so that's kind of like the one thing that's keeping us from using internally but I'm there there's work on having that implements or as a separate library from Scalzi so so yeah I recommend taking a look at these these tutorials and and just looking a lot I mean the way what one of the ways to learn a lot of this stuff is just looking a lot of different tutorials composing them together obviously Stack Overflow has a lot of you know helpful posts on there as well just combine all those together and you just try it you just just experiment with it and in you know it helps you find some folks that worked with these two to serve the sort pick they're bringing a bit to and ask questions and I mean there's a lot of and there's a really live exciting stuff to be learned in these libraries and finance you as you use more as you use more of these pure functional programming constructs in your code you find that a lot of a lot of your what you're doing is your your your build like a core is mostly a purely purely functional and any anything outside of that would be sort of like what I refer to as the edge a world and that could be sort of handled separately from that so you're trying to pull as much of your logic outside of that sort of messy you know environment we have the you know accidental complex a pulling as much of that into your pure functional core so that once you do have to tackle those those sort of you know knowing like you know deployment related issues issues with like interfacing with other API is judge then at least you've you've taken all your core logic and moved it away from that so so attacking these to be separately so so yeah definitely take a look at these resources and yeah once again we are where we are definitely hiring we're looking for you know if this this sounds exciting you this type of stuff now let me know and definitely take take a look at this the open source that we have I mean there's a lot of really really interesting really interesting projects there that yeah so so any any more questions anyone oh yes what sorry I didn't hear yes so let's see so I have so some of this code I have in like my my repos or their presentation for the code for your presentations in there you might find that there I'm gonna I'm gonna see like how you get the slides available say but here can can check out the slides of this too is that that answer your question yeah so yeah so I mean this uh yeah I'm gonna I'm gonna me it's like post in like meetup comments or something yeah so in any more questions I mean obviously you're making great use of just a general question for you about kind of that middle zone between there's the messy edge way over here and right in that and you have these kind of way at the core you have your central kind of building blocks like this yes there's this huge stone in the middle that I struggle with it's not quite either of those if you're talking about a lot of just kind of munching through the business rules and transformations and you know way bigger than just these little building blocks and this kind of it reminds me of a problem that I identified years ago with C++ where the examples were so beautiful and so perfect if you're building a stack right well but in the business world we assume the stacks exist and so you know how that features the language features translated in these larger concepts than just a DSL or just a parser just a you know a new stack or you know these kind of smaller data algorithms and constructs that's the zone where I look at some of the code I'm writing right now it's like I know it could be more graceful but should it all be abstract enough to you know just these beautiful applicators and moan at transformers and where's that so so the way I see it is buried in in that I I know you're talking about so you see it's talking about the middle ground between where you have your your core where you can create a pure functional system if you will quite easily and then you have like the outside we have all the you know sort of messy edge of world stuff but in the middle there's a zone that's kind of a sort of a sort of a it's an area where it's a little bit unclear how to split these apart into two - you know decompose it the rules into two pieces out you actually pull into your functional core right right the way I would describe that is I would say that buried within that there you you could pull these you could actually take to put a lot of this some of this some of this is likely you know it's it's not as not as strongly type not and it could be difficult expressed that the you know using these different category three constructs but I'd say that you might be surprised how much of it you can and a lot of it really is a matter of just spending a lot of time and and believe me it's when you're actually trying to apply using categories are you actually solving real problems that that that has a sort of inherent messiness in there it's the Denali I like to use is it's like it's like the difference between driving a car from here to LA versus flying a 747 if you're driving the car it's like okay yeah I'm doing work I'm turning I'm an attorney and I get on the freeway here and is it going to go along where's again the 747 you take off it's like okay just looking around it's like you don't have a frame of reference you you don't you don't think anything's happening but actually a lots happening you're actually moving you know you know there's like eight hundred miles an hour or something you're moving about the speed your movie at speed of an airplane and and that's that's actually what's really going on is you're working with these concepts you're dealing with higher-order problems and you're trying to shape it you're shaping your problem in a way that's in in that context so once you do everything just just fits together and and it's it's really it sounds like sort of a magical thing but as you're working on it it makes sense you see how things are factored into these different pieces and it just comes together but it does take more time to do that and it's it's there's there this frustrated frustration in the learning process and the cose of these concepts are are quite you know it's it's a different way of thinking it's not it's not like you're the parrot of world where you're thinking in sort of a step-by-step fashion no it's it's a bit more mathematical you're constructing expressions and building them off of each other using a lot of recursion and and yeah so it's it's it's a mental shift and it takes a lot of it takes a real dedication and but but but yeah I I do strongly believe that you know that middle ground can be totally totally split apart where you have parts that yeah definitely this is your edge of the system where it's not not as strongly typed can be you know it's it's kind of like the outside of your your core and then you have the parts where actually it goes within the core of your application so oh sorry yep excellent question yeah so I so I actually when I when I was applying this to building a DSL I was originally whiny as a free monad but it seemed that the Kleiss lis seemed to be a more proper representation and I I think one way to look at the difference between the two is a Kleiss Li Kleiss lis composition building cly sees composing them together that that is sort of that could be seen it's like sort of a declarative process or that you're defining your functions you're composing them where's a free monad is it sort of unwraps that into a more imperative and I mean imperative in a functional sense because I mean is that makes any sense I so I mean why am i imperative in the functional sense I mean I mean like you look at a for comprehension that's that's how you're building a free monad or a free monad interpreter I should say and what that does that's just a that's just like a sort of syntactic sugar for a series of flat maps in the map at the end so so it's it really fits itself towards problems where it's like you're you're you're actually trying to represent something that's inherently step by step but do that in a functional way and for like building expressions not really I mean xx you know a - you didn't like X to the N plus yn equals Z D and that's trying to represent the step by step way is kind of you know it doesn't quite fit so that's why that's why I kind of went towards Kleiss lis you you could certainly combine I mean you could have like your free monad generate cly sleaze I am just sort of top of my hand how this would I mean I I think yeah I mean you could do that if you're like saying like your output of a free monad is like this constructed Kleiss Li maybe I know that sort of sort of hand wavy I know but is that the answer your question okay so any anything else sir oh well as a DSL dis so my use of Kleiss Li here I I haven't seen I actually haven't seen Clive sleeve use for building a DSL it just seemed like a good ideas I was constructing and actually worked out really well and I'm using it for you know you know processing some rather complex data and it actually worked really well my as I built this thing as I found the bugs that I'm dealing with it and there's this project I work on that and use this at the core they have very few bugs in the core but a lot more issues kind of on the outside like just trying to you know it actually there's a that spark comes into play here too and there's like some other issue I ran to their out that that really is kind of separate thank you sir that kind of edge of world but you know to answer your question that there might there might be some out there I'm not really sure I would say like a good is sort of what inspired me to take a look at Kleiss Li beginning was a funnel project that was before his open source but it's open source now so you can take a look at the you know it the github page for for on cue they have that you could actually take a look at them see how I see I'm never use all over the place and funnel so I would definitely take a look at that so so we good or hey cool thank you