 Hello everyone. I'm Satish. I work at Flipkart in the ads tech team. Today I'm here to talk about our experience in using functional programming constructs to build an ad serving engine. So here is a rough agenda for today. Like for those of you who are wondering why Flipkart and ads, I'll just give a brief background. So Flipkart displays advertisements on the app homepage and the desktop homepage. And we also display ads on different category pages on the app and desktop. We also show product listing pages along with search results. And after that we'll briefly talk about some problems with imperative programming and how functional programming addresses some of these concerns. I'll give a high level overview of ads serving both from a domain perspective and from an architectural perspective. After that we'll just go through different problems in advertising and discuss functional solutions for these problems and see how Java 8 solution compares with Haskell solutions for these problems. Along the way we'll also talk about some abstractions and some concepts of monoids, functors, monoids and how they apply in real world functional programming. We'll just rinse and repeat and apply the same procedure on different problems in ads domain and continue like that. So before we get started let me just explain what I mean by real world here. In the real world most of us are working in imperative languages like Java or Ruby or JavaScript. And this is what real world Java looks like. When Java gets lambdas you expect to see code with fluent APIs that looks like this. But in reality you're actually looking at code with nested loops and conditions thrown in random places making it very hard to reason about the code. We spend most of our time thinking about the how part of the code instead of the what part of the code. Actually the code is not visible I can change it. Can you just confirm if the code is visible? So dynamic programming world is no different either. With Ruby's metaprogramming you expect to see code that reads like English sentences like this. But most of the time your writing test which should have been checked by the compiler or your writing put statements for doing debugging or you're just wondering where that magic method indexed by ISB and came from. Again in JavaScript there are these hundred different frameworks and you expect Angular or React to solve all of your problems. But in reality you're spending most of your time writing console.log statements instead of writing actual code. Unless you're lucky enough to work only with Aka in Java or Elm in JavaScript, this is state of most non-functional languages out there. Let's see how functional programming addresses some of these concerns. One day a friend tells you about the magical world of statically typed functional languages like Haskell. He's so excited about its features like referential transparency and lazy evaluation. You are equally excited and you start reading Haskell books to find out for yourself. One day you stumble upon this code for quick sort. This is the most elegant code you've ever seen and you cannot imagine any way in which you can write code like this in any of your favorite languages. So that's the promise of statically typed functional languages. In a parallel universe a different friend tells you about the magical world of dynamically typed functional languages like Lisp. He or she is so excited about its features like homo-econicity. It seems promising and you want to find out for yourself and you start reading books on Lisp. And you stumble upon this code for infinite Fibonacci series. You cannot imagine any way in which you can express an infinite computation like this in your favorite language. You're all excited about Haskell and Lisp and you're dying to use it in your next project. But in reality we often end up with other arguments but not using a functional language at work or in your projects. So these arguments are often along the lines of Scala or Clojure is hard to maintain or Haskell or Lisp is not on JVM and they're also in the form of time. In spite of all this learning functional programming is still worth it because like Alan Perlis says here it changes the way you think. And even if you're working in a non-functional language using functional style still has a lot of benefits. So in spite by all this we've tried to build an ad serving stack at Flipkart using functional style in a non-pure functional language like Java 8. I'll just talk about our experience in trying to do that. So the ad serving system that we're going to talk about deals with three different clients. It serves ads for apps, desktop and exchange. And in each of these clients it serves ads in multiple formats. The ad which you're seeing here is a brand story ad on the app and a carousel ad on the app. Similarly in the desktop you're seeing a carousel ad and a widget ad. Exchange here refers to showing ads on a third party publisher. And this is how a typical ad serving stack looks like. So advertisers configure campaigns and there are ad requests coming from app desktop and exchange. Ad server handles these requests and forwards it to ad selector. Ad selector is a component which selects relevant ads, ranks them and returns an ad. In some stacks the ad server and ad selector is a single component. In some stacks it could be like separate components. The ad gets rendered on the app and the app fires an event to the tracker. Tracker records these events and that's how most ad platforms make money. For the rest of this talk we are going to zoom in on the ad selector component here which is where we mostly adopted functional style. So the process for selecting an ad typically involves these steps. So relevance selects the relevant campaigns and banners for a specific ad request. Attribution performs an A-B test to find out how effective an ad is compared to users who have not seen the ad. Ranking basically scores the banners, ranks it and sends the top rank ad. The duplication is here to serve unique ads if there are multiple ad requests coming in the same request. And finally there is selection which returns the top rank ad. I'll be using these conventions throughout the talk. I'll just do something to fix the Haskell code which is not visible here. So the conventions will be anytime a functional concept is used or introduced in the slides for the first time, you'll be seeing this yellow sticky. Any Haskell code example that I talk about will be in this theme and any Java code example that I show will be in this theme. I'll just fix the code in a minute and get back. Okay, let's just look at the data model for ad serving before we start jumping into code and concepts. The primary entities that we're talking about here are advertisers who have many campaigns and campaigns which have many banners. Campaign has these properties of pricing type and price. To give you a very high level example, the industry standard term for this is CPM which stands for cost per mile or the price that advertisers are willing to pay for thousand impressions. And the banner has these two different properties which are CTR and score. CTR here stands for click through ratio. It's basically the ratio of number of clicks that you get to the number of impressions that you serve. And slots are often group slots here stands for ad slots. And these slots can optionally be grouped in group together. For example, if it's a carousel ad, all the ads, ad slots in that carousel belong to the same group. And a user here has certain properties based on which he or she can be targeted for the specific advertisement. So what I've tried here is to represent the data model using Haskell's type constructors and data constructors. I'm using Haskell here for conciseness and to show how it compares with Java code. So as you can see here, there is a type alias for price and there is an enumeration for pricing type. I've defined data types for advertiser, campaign and banner with all the relationships. I've defined data types here for slot and slot group. And the user here is either an unknown user or a user whose ID we know. And this is a corresponding Java code for the same data model with libraries like Lombok and Java. Even Java code comes pretty close to this in terms of conciseness. You just have to define the properties and just like Haskell showing, just like in Haskell if you implement EQ and show, you just have to do that declaratively here as well. Now let's talk about the different problems in ad selection flow. The first step in ad selection is relevance. And this layer basically selects relevant campaigns. And in this example, let's say you have a Gillette ad, a Loreal ad, Ole ad and an ads advertisement. This layer helps you in showing the right ad to the right audience. The user targeting which I'm showing here is just one of the many, many features of relevance. And we'll just talk about some certain, some example filters for relevance. So one of the filters is to check if the campaign itself is active or not. The other is to check if the campaign is targeted at a specific slot or not. The other is to check if the campaign is targeted at the user's gender or not. In reality, there are a lot more attributes, but for demonstration, we'll just stick to these three. So the first step in implementing this is to define the type signature of it. The analogous thing to do in Java world is to define that, define the interface definition for this functionality. So the type signature for relevance, relevant campaigns here takes a context as input and returns a list of campaigns per slot in a map as output. You can think of context here as some kind of abstraction for user and the request data. With this type signature in place, the, with this type signature in place, the filter, the filters that we want to implement for relevance can all be represented as lambdas like this. So checking if a campaign is active or not is straightforward. You just check the campaign status. Checking if a campaign is targeted at a specific slot or not is like comparing the slot of the request with the slot of the campaign. And gender targeting here happens to have like two different cases. A campaign is either targeted at a specific gender or it's, or it's not. So the maybe type in Haskell or the optional type in Java is perfect to represent both of these cases. The lambdas which I showed for relevance filters can be represented in Java using these predicates which just translate one to one. The next step in finding relevant campaigns is to somehow combine all these filters and then apply it on an input list of campaigns. One way to do this is to just chain all these filters and then apply them together. The campaign.getall method which I'm showing here is just for demonstration and in a real world that is terribly inefficient if you're just doing it on all your campaigns. Another solution instead of chaining these filters is to call an AND method on a filter and then combine these filters. This is not same as function composition because in function composition you have two functions with different type signatures. In this case all of these lambdas or functions have the same type signature and return a boolean. So this AND method helps you combine one filter with another filter to make them work together. So with this kind of abstraction in place adding a new targeting feature is now as simple as adding another predicate to the list. So let's say you want to implement location targeting for campaigns. You just implement a lambda for location targeting and then just chain it here. I'm just simplifying a lot of things here saying it's all AND but in the real world it could be a combination of AND and ORs. So the next step in ad selection is to select relevant banners for the campaigns that you've already filtered out. So to give you a very small example for this like let's say you have an ad request and you have a small size banner, a medium size banner and a large size banner. So this layer basically helps you in showing the right banner for the right request. And the matching the filtering criteria here could be in two ways. One is you either match the template or you either match the dimension. So template here is a concept that is often used in advertising in apps where a banner either fits a template or it doesn't fit a template. In the example which I showed earlier, brand story has a different template and carousel ads have a different template. So just like before the type signature for relevant banners here is a function which takes a list of campaigns per slot as input and returns a list of banners per slot as output. Again the filters for relevant banners can be expressed as lambdas. For template here is a lambda which checks if the banners template matches the request template. For dimension is a filter here which checks that the banners dimensions match the request dimensions. Let's say we want to combine these filters using an OR function. We can just combine these predicates using an OR. So far what we've been doing is to take functions that return booleans and then combine them using either an AND or an OR. So let's just see if there is a better way to combine functions that return booleans. These are some operations that you can do on a list or numbers or booleans. What is a common pattern here? Like I'll just give you a short time to go through this. So the common pattern here is the first common pattern here is identity. Like if you add an empty list to any list you get back the same list. If you add a zero to a number you get back the same number. If you add a true with a boolean value you get back the same boolean value. The next common pattern here is associativity. The order in which you combine these operations does not matter. So it turns out monoid is an abstraction of this common pattern. Let's see how monoid helps us in combining functions that return booleans better. The basic idea behind a monoid is this. Monoid is a type class and if your class implements a monoid type class and if you define what is identity and if you define how to append two things you get concatenation for free. So it turns out some classes can have more than one monoid. For example numbers have two monoids because the identity for sum is zero and the identity for product is one. Similarly booleans have two monoids all and any. That's because the identity for an AND operation is true and the identity for an OR operation is false. With this abstraction in place now we have a way to combine multiple functions that return booleans. Like let's say you had implemented the code for relevant campaigns and relevant banners in Haskell. Your original solution would have been to apply all the filters and then fold it using an AND value or an OR value or an OR function. So with this abstraction in place you have a list of filters which return either an AND or an NE and then you just concatenate them to combine all these functions that return booleans. The next problem that I'm going to talk about in the world of ad serving is ranking. Like let's say there are multiple banners in a slot. Ranking helps you in scoring these banners and then once you have scored these banners it helps you sort the banners and return the best banner for that specific request. Scoring here can be based on multiple criteria like price of the banner or the CTR of the banner or the conversion rate of the banner. I'll just show it with a small example here. Like let's say there is an ad request coming for r slash programming subreddit. There are three competing banners here from Try Closure, Try Erlang and Try Haskell. These banners are scored based on some criteria and Try Erlang gets a score of 0.85 here. Try Haskell gets a score of 0.75 and Try Closure gets a score of 0.65. After scoring, Try Erlang gets to show its banner on r slash programming. This is basically how ranking works in the ad world. So let's just repeat the process that we've been doing for relevant campaigns and relevant banners and define the type signature for ranking. The type signature for ranking here takes a list of banners as input and returns a list of banner scores as output per slot. The banner score object that I've shown here in the output is not just a score or a decimal value. It is a composite object containing both the banner and its score. One thing if you notice is that there is a common pattern here between relevant banners and ranking banners. The pattern is that both of these methods take a map as input and return a map as output. And if you see the type signature, the key remains the same in both the input and the output. The only thing that changes is the value. So this is very easy to abstract with a higher order function that takes another function as input. So this is a function that is commonly available in most libraries. Like if you have a map data structure in Ruby, there are libraries which give you a function like map values which transforms only the values. But it turns out in Java, map itself does not have a map function. So this is very easy to implement on your own with a higher order function like this, which takes a transformer function from key A, sorry, value A to value B as input. So with this abstraction in place, all of your implementations for relevance and ranking simplifies to calling map values with a function that does the transformation. Now coming back to scoring, you can score banners using some combination of all these different scoring criteria. Like let's say we choose price and CTR in scoring a banner. One simple formula that we could use is to assign 50% of the weight to price and 50% of the weight to CTR. In real world, this might change a lot. It can even be a product of price and CTR with some exponential coefficients. So one simple way to accomplish this is to take all the prices, take all the CTRs, zip them, and then apply a function which computes the score. The only problem with zip here is that it wouldn't work if there are more than two attributes, but you can still write other zip, zip three or zip four kind of functions which work with more than two attributes. The map values here is an example of a higher order function which takes another function as input. The scored function here is a first class function. It's only a coincidence that this function is being returned as the output of another method. You might as well assign it to a variable which is actually what a first class function is. We had a real use case where we had to rotate banners if the scores are equal. So to give you an example, let's say programming subreddit gets an ad request and there are three competing banners and all of them have an equal score. You have to rotate banners like this for every request. One simple way to accomplish this is to write a comparator function like this. This is like writing a function which compares people by their last names and if the last names are equal, you compare them by their first names. Compare can again be represented as a lambda. It turns out the monoidal properties that we looked at also apply to the ordering object in Haskell. The ordering object in Haskell is very similar to the output of compare. Like Java's compare returns minus one, zero and plus one and Haskell's compare returns less than equal to or greater than. So the way this ordering works is whenever you try to append to ordering together, if the first comparison returns a less than or greater than, it is going to directly return. Whereas if the first comparison returns an equal to, it is going to apply the second comparison. So with this, comparing two equals scores, sorry, comparing two equally scored banners simplifies to this. You first compare their scores and the operator that you see here is the monoid append operator and then you compare the banners by some random attribute. So this will help you in easily rotating banners if the scores are equal. We will still talk about another problem in ranking. So one of the criteria which is used in ranking is CTR. CTR as I mentioned earlier is the click-through rate. It's a ratio of clicks to number of impressions. And most often this CTR is predicted using a machine learning model. I'll just skip the details of the offline model for this part and talk only about the online model. So this CTR prediction model is a logistic regression model. It takes a list of features as input. These features are often properties of supply, demand and user. An example of supply here is the slot ID. An example of demand here is the campaign ID. An example of user features here could be the gender or location of the user. The output is a predicted CTR, which is a probability from 0 to 1. This is the formula for calculating predicted CTR. It's very similar to the linear regression formulas that we know about. So the p-tas here corresponds to the model coefficients. The x's here corresponds to the Boolean values of the features. The p-CTR or the predicted CTR here is computed using a sigmoid function on this to return a value from 0 to 1. I'll just walk you through an example of a model evaluation and see how we can apply function programming in this. So let's say there is a request coming for a slot 123 for a campaign 123 and let's say we know that the user's gender is male and we don't know nothing about the user's category affinity. So one way to apply this p-CTR function is to take the model coefficients for slot 123, campaign 123, gender male and since we know nothing about the user's category affinity we pick the default category feature and once you apply all these coefficients you come up with a p-CTR whose value is 0.7%. Even though I've showed only like three of four features here in reality there could be a lot more features going on behind the scenes in computing a p-CTR. So I'll just walk you through the implementation of one of these features and then the same thing can be extrapolated to all the features. So there are two kinds of features here. One is a single valued feature and the other is a multi valued feature. Gender is an example of a single valued feature. And category affinity is an example of a multi valued feature. In case of gender we either know the user's gender or we don't. So this can be represented using a maybe type and we can pattern match on the gender and if it's nothing we generate the gender colon default feature and if it's a value which is wrapped in just we generate the gender colon male or gender colon female feature. So the way to combine this for CTR prediction is to generate all of these features for all the attributes look up all the coefficients and then reduce the value to a p-CTR. What you see here is Haskell code for this comparison and what you see below is the Java code. This can be done by calling a map method on Java's optional and Java does not have, Java 8 at least does not have pattern matching by default. This is the same implementation for a multi valued feature where you could change the container to a list instead of a maybe structure. So to understand how the map here on optional works in Java we'll just take a short detour to understand how functors work. So I'm going to borrow the explanation of functors from this blog post right here which has a very good explanation of functors applicatives and monads. So let's say you have a function called plus three. Plus three is similar to plus one or increment function. You can apply it on value two and get a value of five. So that's normal function application. But let's say I'm going to use the box analogy here for most of my explanations. Let's say the value of two is wrapped in some kind of box. The box here could be a maybe structure or a list structure. If you try to apply plus three on two in a box it is not going to work. So functors basically apply a function to a value in a box. So if you take the function plus three and apply it to two in a box using an F map, F map is going to magically get the value two from the box apply the function and return the value five again in a box. I'll briefly explain how this works for the gender feature that we just discussed. Like let's say you have two kinds of user, two kinds of requests. In one of the requests you know nothing about the user here. In the other request you have a user and you also know the gender of the user. For simplicity I'm just keeping the user as a maybe here and gender as a normal string. So if you try to generate the gender, gender feature using this it won't work because gender is a function that takes a normal user and returns his or her gender. Whereas what we have here is a maybe object. One way to make it work is to apply F map on it. So F map is going to help you apply the gender function on a maybe user. So that is exactly what we have tried here when we did a map function on the gender. This is going to work for both kinds of requests with or without a user and generate the corresponding model features. Another interesting problem in ad selection is explore or exploit problem. So the canonical name for this kind of problem is called a multi-arm bounded problem. Like imagine you're a gambler in Las Vegas and you have n arms and there are n slot missions. Each arm here corresponds to the number of options that you have available every time you decide to play. And each slot mission here comes with a reward and the reward is a probability distribution of how much money you can expect to make. As a gambler you have like two options in front of you. One is you can choose to always exploit by choosing the slot mission with the highest reward or you can choose to explore to learn more about the other slot missions. The explore, exploit, dilemma or tradeoff here is how often you choose to explore versus going for the machine with the highest reward. It turns out this has a direct analogy in the world of ad serving. The arms here corresponds to the number of banners that you have available and the reward here is the number of clicks or score that you expect to make. And you can often decide how often you want to explore versus how often you want to exploit. The implementation of explore will probably consist of something like this. It'll have a function which decides when to explore or not and whenever it explores it goes with a different kind of scoring logic and whenever it is exploiting it goes with the actual scoring logic. One simple solution to explore is to order the banners randomly and this can be done by writing a function like this which computes random scores and uses those random scores instead of the actual scores. The other problem in advertising which I'll talk about is deduplication. So deduplication serves unique ads if there are multiple ad slots. Like in the carousel ads which we saw earlier you could end up delivering duplicate banners or you could end up showing duplicate advertisers or duplicate campaigns. An example of deduplication is something like this. In this specific example we are showing two Marvel movie ads and one DC Comics ad. According to dedu, this is wrong because you're showing two banners for the same advertiser. dedu actually fixes this by showing one Marvel ad, one DC Comics ad and one Hellboy ad. One way to implement dedu, the type signature for dedu takes a list of banners scores as input and returns an optional banner as output. It's optional here because at the end of dedu you can even make, end up making the whole slot empty. So it's representing either whether a banner is present or not. Dedu itself can be implemented in a lot of ways but first we need to check if dedupe is required or not. One simple way to do that is to check all your slots count the number of unique advertisers and match it with the number of unique banners. A simple way to do that is to collect all your banners across all your slots and then group them by their advertiser using something like a group by method and match the advertiser count with the banner count. The data structure that you end up with here is a grouping of banners by their advertiser and that can be used for actually implementing your dedupe logic. One of the other problems in dedu is that you want multiple options. In some cases you're okay with duplicate ads. Like in the case of exchange you're okay with duplicate ads and you don't want dedupe. In some cases you want dedupe by advertiser which is the Marvel vs DC example which I showed earlier. And in some other cases you're okay with showing duplicate ads for the advertiser as long as they belong to different campaigns like different Marvel movies. One way to represent all of these complexities is to have a function of banner and then wrap it in a container. Like if you don't want dedupe you can just set the dedupe criteria as nothing. If you want dedupe by advertiser or by campaign you create a function of banner and then put it in a container. This criteria will be used at some point in your dedupe logic. And one way to apply function which is wrapped in a container is applicative. This is, again I'm going to use the box analogy here. The way in which this differs from a functor is that in case of functor you only have the value in a box. In case of applicative you have both the function in a box and a value in a box. In this example here you have plus three in a box and two in a box. Applicatives extract the value from both of these boxes, apply the function and return the value five in a box. And one of the things which I skipped earlier is attribution. So attribution here is like an A, B test to find out how effective an ad is. So to give you an example like let's say there is a halo ad and there is a group of people in A who have not seen the ad and they are one person likely to buy the game. And there is a group of people in B who have seen the ad and who are 1.5% likely to buy the game. So attribution here helps you in doing that A, B test and in measuring how effective the ad is after the end of the campaign. The type signature for attribution looks something like this. It takes a list of banners as input per slot and returns a list of filtered banners as output for the same slot. Let's just say for example that you have an A, B service in place and it is quite expensive to call the A, B service once every banner and you want to do it together for all of your banners. One simple way to do this is to take all the banners from all the slots, flat map it and then pass the list of banners to the A, B service. What flat map is doing here is to convert a list of list of banners into a list of banners so that you can directly call the A, B service only once. Let's take another short detour. What we've done right now is to apply flat map to convert a list of list of something to a list of something. In the case of CTR model feature we talked about the case of handling if gender is present or not by doing a map on optional. There is also another case where the user itself may be present or not. Like to give you a small example if there is an ad request coming from app or desktop we may not know the user in all the cases. It might even be a new user. So we have to handle the case of user itself being present or not. So this is done in Java by doing a flat map on optional. So is there any similarity between the flat map that we do on optional and the flat map that we do on streams here? So what if we try to use a map instead of flat map here? The problem is if you try to replace that flat map with a map it won't even type check. Let's just see why. So this is the same code in Haskell. So what you're seeing here is a request. The user object is a maybe user. The gender object is also a maybe gender. User is a function that takes a request and returns a maybe user. Gender is a function that takes a user and returns a maybe user. Let's say you want to find out the gender of a maybe user. If you use fmap on it it is going to return a maybe maybe gender. Whereas if you use this magic function right here it is going to return the gender wrapped only once. It turns out monad is an abstraction of this common pattern. I'll again use the box analogy here. Like let's say monads help you apply a function that returns a value in a box to a value in a box. So let's just say you have a function here which is a half function. This function takes a number as input and returns half of the value as output. In this example it works only for even numbers. It doesn't work for odd numbers. For even numbers it returns a just 10. For odd numbers it returns nothing. Let's say you want to apply this function to a value 20 in a box. Let's just see what what all happens if we use different different function application operators. You cannot apply the value 20 in a box directly to this function. That won't type check. Whereas if you apply fmap on this function you're going to get the value 10 wrapped in the box twice. The box here is just a container like maybe or just whereas if you use the monad bind operator on this it is going to extract the value 20 from the box apply it on the function and return the value 10 wrapped only once. Let's just see how this applies to the same gender feature generator function that we're using here. So we have a maybe user which is the value that that you get by calling context.getUser. And you have a function here which takes a normal user and returns a maybe gender. If you you can't directly apply it again because maybe user and user won't type check. If you use fmap on it fmap is going to extract this user apply it on this function and it's going to return a maybe maybe gender which is exactly the case that we saw here. Whereas if you use the monad bind operator on this it is going to extract the user apply it and return maybe gender wrapped only once. Finally we need to tie all these steps that we have together to create a workflow. So in reality there could be multiple workflows like you could choose to have a different workflow for app and a different workflow for desktop and all that. It turns out a workflow is nothing but function composition. The function composition operator takes a function from B to C as input and another function from A to B as input and returns a function from A to C as output. This is a list of functions that we have so far. I've simplified it to remove the map and the slot but bringing it back is still the same thing. So we have a function called relevant campaigns which takes a context and returns a list of campaigns. We have a function called relevant banners which takes a list of campaigns and returns a list of banners and so on. All of these functions can be composed together nicely to create an app selection workflow which is not a lot different from the workflow box that you're seeing above. That's all I have. Questions? What I've shown is just using Haskell for prototyping and then trying to understand whatever we've done in Java 8 using Haskell's terms. We only use Scala and Closure in production. And by the way this is all in Java 8 this is not in Haskell in production because of No, in reality it was not prototyped in Haskell and Java. We've done analogous work in Java. Like we were far more productive in terms of adding more features. Like once we have the foundation in place it was pretty easy to add features. Like if you have if you're representing all your features as lambdas or if you're representing all your model features as these optional types in lambdas it was very easy to add successive features on top of that. Like basically we were trying to use whatever we knew from Scala or Haskell and trying to apply the same thing within the constraints of Java 8. Is there an advantage to move for moving to Java 8 when you get most of these things with Lomborg and IntelliJ and other things right? So Java 8 has a lot of other features which are which make it more functional than Java 7. Like the options of streams lambdas and optional types are native in Java 8 whereas they're not native in Java 7. So Java 8 still gives you a lot of benefits in trying to use functional style compared to Java 7. It's still possible to achieve all this in Java 7 by writing your own implementation of optional or Goa's implementation of optional and things like that. But Java 8 still gives you a lot of native benefits. Yeah. Which one are you talking about? Okay. The data model, sorry. So this is not possible in Java. You have to represent this as a this is kind of like an enumeration. So the way to represent this in Java is to create a maybe user and when that is nothing you treat it as an unknown user or if it's a just user you treat it as a as an actual user. So did you explore say functional Java or Java slang or one of those functional libraries that give you some of these type classes say monoid so they have they have sort of implemented those type classes. We've used only a library called Proton Pack. So that gives us features for doing zip and pattern matching. Proton Pack. Thanks everyone.