scala.bythebay.io: Ryan Delucchi, The False Economy of OOP
Recording: scala.bythebay.io: Ryan Delucchi, The False Economy of OOP
um so today uh we will be talking about the the false economy of oop and let me just kind of give you a quick uh quick introduction so i'm actually working on the go 90 team at aol um which uh it's just a quick historical things actually uh uh aolo was purchased by verizon last year so it's this same scala team that that was actually uh under verizon when half the team was transferred aol so um so uh yeah so i'm going to uh we're going to be talking about false economy of oop and this this title probably gives you some ideas like uh about my perspective is um um so i i am actually i've over time i've actually grown to be a a fan of uh fp and you know some might even call me call me a bit of a zealot i guess but but i've actually started very the first days where i started uh getting into programming it was back in the c plus plus days i was actually really excited about object-oriented programming so i've i've completely come full circle here and the idea of this is to really give sort of a journey through my how the thought process and you know take all of you along to see where you know where how how i've arrived to this conclusion about oop as a model for designing software all right so um now the the skull language which is obviously your focus at this this conference um it it the one of the main goals of the skull language is to bring both oop and fp together cohesively into into one language and so when it comes down to actually bridging the worlds i would say that it's been a resounding success i mean all of us here or you know many many of us have happy work in scholar for a while and have found it to be a great great language for solving complex problems and and so so just keep in mind that there there's the the objective of scallop to combine these paradigms together um is i definitely see it as a success and the focus here is really more about more about oop by itself because i mean while you can you you can write software now effectively using both paradigms we have to answer a question okay should we should we be using oop for solving a lot of our problems and oop has been around for quite some time uh simula 67 first used the notion of of an object and that's kind of that's why many see that's where oops sort of started small talk is a com commonly language that people usually tribute as as the first language they're really to really uh dive into pure oop as as an approach but uh over time i'd say you know as as loop has progressed in the industry we've found ourselves with a lot of code based become really complex and hard to work with and this is this is what has really led a lot of startups new startups to start adopting fp languages and the hope really is to to leverage the fact that the language lot has a lot more of a concise syntax and maybe if we adopt a language that that allows you to write code more more succinctly and express concepts and in a more in a more um i should say you know a concise form you know maybe in the long run in some of the the initial investments will will out will be uh will be compensated for the cost of that that the the cost savings but as time's gone on i'm gonna say i'm sure a lot of folks have heard recently some companies have some major companies have adopted adopted scala have kind of thought oh wait a minute it doesn't seem like we've really we spent all this time trying to acquire talent training people and investing in the new the new tool chains that are they're required for using skull and you have some companies they're kind of sort of backing away kind of wondering okay what you know is is this fp stuff just kind of a sort of an esoteric thing that just doesn't really you know is it really all smoke and mirrors is it just a fad well fp is actually predates oop by quite a bit and it has its roots in uh what is branson mathematics known as lambda calculus to help by lonzo church and this is really kind of a an interesting relevation because um you know as you know as i've you know as i've started programming since since the beginning of my career even even before that i was playing around you know with languages in high school um oop just oop is uh has been has definitely um it's it's been it's been talked about a lot it's sort of like oh this is this you know the ultimate way to model software so this this might be surprising to a lot of people um so so yeah once again the skull language the tool chain is complex and you ha the realization that we have here is well okay if you if you take fp and oop together are you really going to are you really going to get the benefit the true benefits of using a functional language and the answer is and the reality is there are there are fundamental problems with with the oop model when you're trying to create a class hierarchy that models your problem i'm sure many maybe you have have ran into this it it can be really i mean humility may seem like a strong word but it's it's true i mean you you might be like okay this this is this model almost works and i make this i make this change here and it's it's not quite um it just it feels like you get this extinct impression that you're not really fitting the problem very very precisely and and they're usually when you do settle on a good design you think that you've actually gotten you might might have it worked out so if you add features you have some extensibility there but it doesn't take long before it begins to collapse on its own weight so this is not your fault at all and i think this is the realization that i've that has led me to to take a more serious look at fp as a as as approach to focus more on because you know in in admit many places i worked at have actually you know ran into the same problems i run the more people that's that see run into the same issues they you know it's really really common to say okay well is there something i'm doing wrong there's something i'm missing i mean something we keep telling ourselves but with oop that's where the problem is and it really if we were to summarize they were saying okay the contracts that are defined in in your class hierarchies and how you and how you uh you know use inheritance and composition which and and composition i i'm i should just say containment because in fp we mean composition much more precise fashion um but the the the contracts that you're defining are not they're they are vague and really they're the instruments that are there of are actually they're they're they're not precise enough and there's a lot of a lot of you know bouncing around between different class hierarchies and this this this becomes a real cognitive load where you're trying to decide okay well if i you know if i use inheritance here if i if i factor these classes out this way and you spend a lot of time trying to trying to design your class hierarchies so and then you go back to sorry this thing is not then you have design patterns and we've all i think we all have the we have the gang of four book on on our on our desks and how many times do you really how many times do you really find yourself reaching for that and say aha okay this is exactly what i need i mean it happens every once in a while it gets kind of close there but never really doesn't really solve solve the problems precisely so really oop once again oop creates a false sense of understanding it really does i mean you you find you create a new model that mid staff hits your problem and almost well it almost fits it and it kind of creates a sense of okay i've got this and it doesn't take take long sometimes it does over time you find that that you find that your model is just your flexibility just isn't there and this is where the false economy comes from and i'll be revisiting it back as we look at our example here which is optical tape machine so here's our example we have say we have a you know company called tapeco they they have a tape machine that i know that's not an optic that picture right there is not optical i'm just pretend like it is it has an optical head as opposed their read head audio head but but see you have a optical tape machine that reads a tape with color band segments which may have gaps in them and we can pull the machine to say okay what color is under the reed head at this particular point in time so they may be over a gap there's no color there or it's uh it's over color and then we know what the color is so we have to be able to check and see if we reach the end of the tape and compute a score for each color on the tape so here we have a definition of colors here and we have a tape machine i o which is provided to us from tapeco and this i o is pretty simple you just start you can start the machine you can check say okay are we at the end of the tape eot or we can pull it's okay which color do we have so it's an option because if it's over a gap then it's going to return a none now okay now we're going to define our we're going to find our model of this tape machine so we're going to need some kind of a tape reader which is for getting the next color segment we have we'll define this notion of a tape reader proxy to sort of abstract away the i o so say we have one i o another i o we just have one here but let's create tape rear proxy so common thing that would be done when coming up with an object object-oriented design um and also we're gonna have our scoring system for segments and segment groups and we're creating these notions because based off our business roles are listed before you may have uh you'll have different segments of of different colors and then you have groups of segments together because you may have like well you may have five magenta three green um so we're going to group those together so there won't be contiguous groups but we're grouping them together since that's how we're doing the scoring and right okay so here's the scoring business rules i was talking about so we have we have a score assigned for each color um we have the stochastic segment group which is going to be the count of colors that you have there in the color um we're going to have the tape is going to have its own score as well and then we're going to have say that okay well actually a total number of segments based off our problem here is 10. all right those are business rules so we're going to find a tape reader and we define this proxy we're going to read the next segment now this is blocking so when you actually get the tape reader and you again accept you know block and and and actually return what the next uh what the next segment's going to actually be and then we have a tape reader proxy which this is this is where it actually this will not be blocking it'll pull for the current color that's under the tape all right now by the way the difference of these are both options here the first one to return a none if we reach the end of the tape and then the tape reader proxy it returns none if it's not over color currently so that's kind of a difference there so we're going to find the tape reader io and this is going to take this is going to take the the tape machine i o as as a member there so so this is abstracting over the the interface here now you'll notice that we're creating an abstraction here and we have to do some delegation here and we're kind of creating this um sorry this this functionality here that's that's specific to that that for us is specific to tape i o but you see we're kind of we're kind of doing some fairly general mechanics here that could be abstracted out here you'll see that later on so we have like so right here we're actually creating a segment it's all the scoring the segment group and then you know the score color trait now also we're going to be we need to collect these segments and actually you know read segment groups from these and we do this here this by by this way defines the tape machine which has to have a tape reader within whatever class is actually extending this trait right here so now we have a tape reader machine impulse which takes in the proxy and extends the tape machine and actually implements uh actually implements the get next segment in here all right now there's a few improvements we can do we could say that some of the code inside we can make this a bit more functional um like a great thing here we can do here is we could use a scala z task or sorry scarsy stream process where we actually say okay we're turning the segments into a stream it's going to be a stream of tasks of segments right here where it'll wait every two seconds and actually output the next segments as it gets these and return exception when it's done to sort of terminate the process so this is an improvement we can do tightly but after doing that and then of course here we're running it right here just do like segments or that's how you would run the process and then run the task within it and do the grouping so turn into a string but we've haven't really changed the we're we're using fp sort of in in the small here and it adds an improvement at the fine grained level so okay so okay after doing all this now we have this scoring thing that's going on wha what what's going on here why why what is the scoring all about really i mean we saw the business rules um what what what are we doing fundamentally well as we break this down mathematically we have w number magenta's x number of ambers y number of greens and z number of aquamarines and we know that there's a all the the total number of us color bands is 10. all right well if we just multiply all the numbers times 10 okay and then we say we divide these all by 10. well that's the same as this it's an arithmetic mean so all this we've done here is really we're just we've created all this extra machinery to completely obscure that we're just doing an arithmetic average that's all we're doing so okay while we're still doing things that we're modeling things using oop we're going to say okay we're going to pass this color code through all the different colors and we're gonna go here and compute the scores differently and oh wait a minute notice we have a segment class before it's gone now now we just have color represented directly so you start to notice that as you start representing the problem you're starting to get the core mathematical definition of things which the simple arithmetic mean some of your oops abstractions start to go away and you see that here where we have this tape machine implementation now it's not it's i mean you see the only difference so these we go from segment to a color here the segment class disappears we just don't need it anymore all right now let's say we want to modify this tape machine and add the support for representing shapes meaning instead of having like multiple color bands well we can still support that but let's say we also want to support tape that has like a large rectangle on it and we want to compute the midpoint of that rectangle as the score well you know okay so we have to pull some functionality out of the out of this tape machine now we have to create a colored tape machine we have to have a tape with shape machine because we gotta handle shapes now and then we have to go here to create a tape colored tape reader proxy a tape with shape reader proxy and now we have to create this tape rate this this this color i o and the rear shape i o that branches out in separate classes we're pulling some fun common functionality out the tape reader i o has this in common and we abstract it this way but you know to be really honest this is this is code duplication here it's not really i mean syntactically you're you're improving things but semantically you're i mean it's not i mean you have you know you're you're you're trying to fit your model towards a problem by creating these sort of various um what in this case are actual stub implementations but in a much more complicated problem it could easily be a lot more gnarly than this and then you find right here it's okay we have our segment group that that part actually is okay i mean here we're representing the color code the the score for a segment group and a scored shape by finding a midpoint within a rectangle so now we want to do version three ah so we have another version here we have a pull shaped part now this is a version of tape where say we have a tape that doesn't have just a single rectangle in there but instead it has some part of a bigger shape it could be like uh it could be like a half circle it could be a right triangle it could be a square but the whole thing is like one big shape and we want to compute the score of that which would be the centroid of the region okay well how do we compute ceptoids or regions yeah it's pretty simple really i mean we just we do these uh both for the x the x y coordinates have to be done independently you compute the sum of the midpoint of each part multiply times the average of each part and divide it by a sum of all the parts you do that for both x and y all right well all right sorry about that uh spoiler um so we created uh so we have to create case classes for all the shape parts we have to we had to modify all the tape machine reader proxies and we need to we need to implement collecting of shape parts is gonna be similar to colors but different enough to make it annoying now we're gonna try it okay i'm gonna pull some common functionality here so and of course add the centroid computation so oop what is it you do here i mean what's it giving us i mean honestly i mean it's giving us more code to write it give us more abstractions that leak and why why is cd subtractions are leaking because every time we're adding a proxy or we're adding we're adding a new version of the tape machine we have to add these other abstractions of other subclasses like okay how how did we are we really factoring things out separately here just kind of doing some syntactic uh representation you know you know you know manipulations i mean it's it's the abstractions are not really well defined they're not um they're not self-contained at all so they're they're very leaky and as i showed just with the arithmetic mean we were completely obscuring what was really normally a very simple mathematical thing and you know completely obscuring that so all in all oop is really it's a blunt instrument it's clumsy not very precise and what guarantees are being really offered here i mean you're not really defining any rules that the compiler is going to help you with you're just sort of sort of grouping functionality together and factoring it out in in a certain just sort of very rather arbitrary way so and what i talked about before where i said you can fix certain pieces and make it more functional within within your structure well why do you want to do that because i mean now we're sort of if you're effectively quarantining your fp fp usage in tight scopes which you should do so you should do that with immutability and effectful behavior not not with fp so it's kind of kind of backwards in a way so here we're going to show a new model that solves the problems of each of the three versions i talked about but using instruments of fp which are much more precise instruments and it all starts with these three types right here we have the duotype which is simply represents two things some of type a and b now that's an actually that's a shapeless uh h list is what that is and represent that way that you'll see later it's kind of a convenient representation we could have created a case class here that would have been fined i mean i mean if you want to get precise about it you're essentially creating some form of a some form of product type but we're using a shapeless h-list here that seems to work pretty well and we have a normalized type where we say there's some input of some type and we're going to read it in and then transform it into a duo of a and b now the reader the reader this is giving you what that is that that's actually that is a reader monad which is a specialization of the cleisly which is really just a monadic function this essentially is that's what it is um and you'll see that you have you can actually compose these things in a lot more flexible ways and just your you know you know is a basic function and you know we'll show you'll see more in the example but we have this normalization where we're saying okay anything that's in the duo a b representation that's our normalized form okay now we have an evaluate normalize where we say okay we have a value that's been normalized already now we're going to evaluate into some final result or an intermediate result well no sorry it's it's a final result the normalized form is also an intermediate representation so yeah so these are the three types now there's not much hidden here at all i mean it's precisely duo is just representing two things and we have a representation of a normalization and computing normalization that's all that's here there's nothing hidden from view now now we're going to abstract a duo plus or plus star what do you want whatever you want to call it it looks like a plus operation via monoid now amanoid is it's it represents it has two operations scholar z refers them as zero and append um strictly speaking append should you probably just call it an operation because for combining two of one thing and giving you that same type of thing as a result but it's very often used in a context for appending i can give some examples like the monoid of integer addition would be zero would be the number zero and append would be adding them to together you could have a monoid of integer multiplication where the zero would be equal to number one all right and append would be multiplying them together so zero is one in that case because multiplying one times a value is going to give you the same result and uh it turns out that that's actually one of the rules here for monoids where you're saying that you multiply the identity times the value you should get the same thing as multiplying it you know in a different order or the value itself and there's also associativity here too which the two examples i gave you plus and multiplication follows this is associativity rule so your models have to follow those rules and once you once they follow these rules and you have this abstraction here which for the case of duo we're treating both components uh we're training the components together because they mean that they're although they're abstract duo a b is abstract we want to create a monoid that says okay well the a's can be combined together and the b's can be combined together all right this is very abstract but this this applies to all the problems that i showed before in the oop design and they they will all fit this model very well all right so that makes sense so now we have what is essentially the meat of of our computation here it's known as the reduce method and this reduced method actually ties everything together and it it assumes that you have a monoid of duo a b all right it also assumes of course that you have an f which is foldable which you can do the fold operation which is for combining values together in in some fashion and then we're saying that okay f has a functor as well and of course we have some normalization available to us that'll go from input to a duo of a b and in evaluate normalize i'll get go from the duo a b to some e result and then reduce the result will be e so this is for doing our computation and this it's it's kind of funny because it's really a one-liner but we but there's you know there's there's five lines of type declarations now what's interesting about it is you get this wrong it's not going to compile that's your error case so you know you got this right you know it's actually going to oh i see we have a question here we're curious can you actually implement this any differently like can you actually make this compile with anything but this right you can't get it wrong the type system is completely it you're you're you're implementing this on the rails completely and you define your types in this fashion this method right here is going to require all these to be available because well what's what are we doing we're taking a values of i type input f f of i it's a bunch of values f could be a list it could be a set it could be an option anything that is a functor and a foldable it takes those values it maps them to norm which is which is the normalized reader which we defined earlier so what is it doing it's actually normalizing all your values now once we have all the values normalized we're going to fold them using the monoid and then you evaluate them and you get your e all right so that that's all this does it's i mean that's that's everything right there and we're using a functor so i want i feel i feel obligated to show you that the functors have rules as well um you have the identity rule which says that if you have if you're doing a map over a functor f and the function you're using is an identity function which says okay whatever input is the output um it should equal the same it should be the same it should be a no op that's all that says and then if you were to say i have a functor and i'm going to map it using these two functions composed together that should be the same as mapping one and then composing it with the result of the other one mapped so really a lot i mean you can you can represent this in code very simply where it's like okay you have your functions you compose them together versus like do a series of maps now if you broke these rules i think it's pretty easy to see that you know you you have a functor that's just really irritating to deal with i mean it's an it's a bad api it's you know sort of like a microcosm of what happens when you have like a really bad class hierarchy where things are just not composable and fp forces you to follow you know that forces you to use use these rules and compose solutions by by combining them together so okay now we've defined all the machinery we need to do an arithmetic mean and we're going to create a case class called index sum where we say okay we have a sum of all these values in a count of how many values there are now we have an evaluation result saying the results either an error or a double result well let's create a lift int we need a normalizer we need to be able to normalize these index sums into a duo a b which in this case is going to be uh duo int or sorry duo in double um now there's some neat shapeless machinery we're using here where we're saying okay we have this index sum class but remember duo is not it's not a case class it's it's a shapeless h list so this generic index sum.2 that's a handy feature in shapeless where you take an arbitrary case class and convert it to an h list okay why do we do that well we want to have our nice name class that we use on the outside and we want to internally represent things as ageless a very common use case for um for shapeless so we normalize everything in a way shuts out we say if we have like say the number five we normalize it by lifting it into this form what's gonna be five one why we do that well we're computing an average you lift a five you wanna say okay you have one of them so now you have five and one you can compute your average easily you know you just divide them so then we want to evaluate the mean and that's exactly what we're going to do right here we're going to say okay we're going to evaluate this normalized form the duo and we're going to do it by we're by we're going to create a reader right here which is a reader monad and saying okay we have an index sum and count if count zero allow you can't complain you have a divide by zero so that's an error that's why we have this this eval type which by the way is a disjunction um that is uh actually known as a sum type we're saying it's either this value or that value very commonly used for error conditions that's how i'm using it here so you if that's an air conditioner you get the exception left side that's why the dash is there right side you have an actual value you can divide it you're doing your average right there and then we compose it with this other reader that's going to all it does is really converts this thing into a duo and we need to do that because they evaluate norm it expects a duo which once again is an h list so you have to compose it with this reader that does the conversion that's why we use readers to begin with by the way it's really easy to do that i mean we could just say that okay we have have our meat func our meat here which uses this nice index sum type for us and then compose it with something that that converts it to a shared representation that's used with the rest of our code now turns out what if we want to do weighted averaging it's really easy to do that i mean we just need to add this to all the code that we already have it's a weighted integer where we're saying we're creating a new normalized type that says we have a value and a weight and we're going to lift it by saying okay the weight times the value in one that's it we just add support for weighted averages we want to compute a median we can do the same thing we can actually we we define we're going to define a values class this is a little interesting here in that we're actually not using the second part is saying put a unit in there we just need the first half of the duo because all we're going to do here is when we lift this thing single values get turned into a list of values of just one one value um we're going to do the meat compute the median this computes the median right here but once again we don't need the second value there but it all fits the same form centroids a much more complicated example but it also fits this form we have different shape representations now we did a little we wanted to be a little bit cute here and like add some little thing where i say okay well a square is really a rectangle s where the side is the same a rectangle is actually two right triangles so we're kind of doing some composition of shapes here and i introduced the notion of a trampolines here as well just because it's it's i mean if you implement this using recursion right here you could just use recursion but it's not stack safe you have a complex shape you can blow the stack building a really complicated shape here well guess what to implement in a stack state fashion using a trampoline very easy to do it's very similar i mean look at the code you just represent as a trampoline type sits it's this actually the trampoline type is underneath covers it's actually a free monad but you don't have to keep that in mind when you're using this i mean just know i can't building a trampoline i am essentially saying that this is what's returned and this is a value this could be um suspended on each uh you know level of our sort of imitated stack that's gonna be on the heap and then we can write all these different shape functions okay so that takes care of all the shapes uh how far they go ahead here all right and then we have all the operations we're saying okay we're going to we're going to lift have a our normal eyes right here which lifts the shape and since really computing a centroid is another form of an uh averaging it really is we we're doing the same sort of thing where if you remember the formula before you have the you have for each of the different shapes you have the midpoint in the the area and all those are collected together they're summed together okay that's that's our monoi doing the heavy lifting for that and then we have this it divided by a total um summation of of all the all the areas and that's the second part of the duo where that's computed so here we can evaluate the centroid using the same structure and since everything is precisely defined using we've defined a monoid of duo a a well there's nothing to stop us from having a myenoid of duo duo big decimal the big decimal because we have the definition of a monoid of duo big decimal big decimal and we also have a monoid of dual duo big decimal as well because it's it's a recursive definition the definitions are already there we didn't have to define each of these different levels of recursives it's just it just works because all all the monoi definitions are available and by by the way i'm using big decimal because uh you a monoid over doubled is not does not follow the rule of associativity that's because of floating point arithmetic so you have to use big decimal so i have a important thing to consider there um and so they say how you compute the centroid in this fashion so we're following the same structure and we're able to solve all these problems with the same code so once again abstractions in fp they are they're more precise but they're less familiar i mean there's a lot of live new concepts to learn and believe me i'm i'm still learning a lot of them myself i mean it's it's it's a whole it's a very interesting world of a lot of different representations of things that that we've seen already in our code and just probably even just been agitated like gosh there's got to be a precise way to represent all these different patterns and i'm sure you've seen that when you were trying to to build object-oriented code and we haven't been quite satisfied yet well you know scala z cats um these these libraries are collections of a lot of these very concepts that we've been looking for you know since we've begun programming to be honest and it's really important to understand that familiarity is not the same as simplicity okay i mean there's a learning curve no doubt there's a learning curve i mean it's it's to learn all these concepts takes time patience and you know talking with you know folks in the community and and you know just just sharing ideas and knowledge about these different tools that already exist and that's sort of the beauty of fp is that you're not you're not just inventing abstractions out of thin air you're using your effect you're standing on the shoulders of giants and using abstractions have been around you know for you know since you know since the beginning of you know what you know when we were at uh going back and you know study of study various branches of mathematics i mean we're talking about real fundamentals here that we are finally we have a way of taking advantage of these fundamentals so this is really an interesting thing that i've learned just completely this through this through my own discovery and you know journey through learning more about fp is that there is no fp rabbit hole that's a complete illusion and the reason why i say that is because the dji that what happens if you know what what's the worst case scenario as you try to build try using these different concepts and combine these together what's the worst case your code won't compile so you know okay well what do you do then we say okay what i do wrong is start you know doing some you know stack overflow searches and looking at some documentation and you know just you know i mean how did i learn the free moan ad i looked at like five different documents and compared all of them and just did my own example that was different from all five of those and finally started piecing together what okay what this is what a free monad is i mean that's what it's a really it's sort of a messy you know sort of you know rough process to kind of figure all this but once you start seeing how the different pieces fit together it's it's it's pretty exciting and oop on the other hand you you don't have rabbit holes you have gravitational singularity so you can fall in a black hole and i even realize it's like you know i mean okay i'm solving this i got this working but then you have to have this feature layer later and just you end up with this code base that's just a real tangled mess and i mean it's i mean we're humans i mean we it's hard to keep all that stuff all of our heads all that stuff in our heads at one time and that's why i think fp program with fp is really an act of humility i mean there's no reason to be you know to humiliate ourselves in trying to structure things these strange ways when we can just okay there's just much more there's much more precise precise ways to define the different pieces of what we're trying to build here and that's that's really the the central idea now i i really do think that we are in the most turbulent time uh for fp in in the software industry and the reason why i say that is because i mean there's a point in time where fp was all in academia so it's kind of it hasn't really been tapped by the industry yet well now people are starting to get excited about it and you see a lot more companies adopting using tools of fp scholar you know closure lie the fp languages and i think the excitement right now has outpaced sort of where a lot of us are and understanding these core principles i mean you know we you know all of us have have a lot to learn on on all this and and that's that's why i say turbulent because because excitement is there um there's still a lot to learn and i think there's there's a lot of frustration that's why you see some of these companies that that are well dops called and kind of like oh well what would we get ourselves into and it's it's really you know it's just a point that we're at in the industry right now and as a result of that really i mean this kind of silver lining i mean it's there's there's time there's still time for us to learn and it's not you know and you know any at any point where you know where folks are at right now you're pretty much ahead of the game i would say um but to be really honest i i really do believe that there's no point was good this is going to take off in the industry and it's going to it's going to reach a point where everybody's using using fp in some form or another you know it may not be you know i may not be in scholars e or cats it could be a different language um it seems very likely it could be scala i mean just looking at how much interest is in there and how many folks are you know working in skull language seems very likely but i really do see it as becoming a required and essential part of building software in the decades to come and here's a tweet that i posted uh back in march of 2016. and this is in response to people i've hear say oh all this pure fp stuff it's talking a skill on large teams and no i'm sorry i i mean i work at you know pretty large company and we've seen the opposite actually demonstrate in reality so so yeah i mean i i really see that this being the reality down the road i mean i don't know when this will be i mean none of us knows i mean we can't really pretend to know exactly when but i mean it's it's it's really inevitable so all right that's the end of my tape here so i think we have a little bit of time for questions anyone oh oh oh sorry sorry you're right no yeah okay that was totally uh yeah that right sorry about that it's not anybody no all right you