Context Receivers: Kotlin's new secret sauce | Alejandro Serrano Mena @ Advanced Kotlin Dev Day 2022

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
foreign I'm also pretty excited about context receiver so that's why I like talking about this uh yeah it's like I I you know it's it's so cool but it's also going to like end all the battles about di Frameworks I think so it's like it's you know like they should give them like a Peace Prize you know something in the Hague uh uh but yeah this context receiver just to give you an idea is this new coding feature and I will yeah describe a lot of things about this uh actually it's like the coolest feature in 1.6.20 and 1.7 and all of that uh and well unfortunately it's still experimental so in theory you shouldn't use it in production and I think you cannot use it in Godly native multi-platform but it actually covers a lot of use cases mostly for all this cross-cutting concerns in your in your uh software so I will go uh through a few use cases of how you can use contact receiver uh from uh what you should be using it for like dependence injection to what you should use it for if you're a crazy scientist like uh error Scopes and uh new type classes and things like this so that's that's mostly to show how powerful this kind of thing can be and also you know to show you a possibility of how kotlin code can use in the future maybe you know try to figure out together whether this is how we want golden code to look like or maybe this is just some crazy ideas uh in my mind who knows anyway as an example I will use logging which is this prime example of what you what we tend to call a cross-cutting concern and that's something which you know has to happen in your up in your code right but it's it's never part of the main domain like unless you are building like a login application but most of us are not building this kind of things but you still have the logger so it's not part of your domain you actually don't want to be super excited about it because then it will pollute all the code around it right it's it's like it has to be there but you would like to hide it as much as possible because it's not part of your business logic uh and actually login is something that somebody writes in a library and you reuse in a bunch of different uh other application or domains so this is as I said something and you can think of of login I don't know instrumentation uh a bunch of those things uh usually are like cross-cutting so in this case I will model this quite simply just an interface uh with a function log and it has a bunch of stuff exactly the three arguments I need you to show my examples which are like the log level uh the subject and the message and uh if you want later on launch we can talk about why I put the suspend there I mostly every time I do something which may involve input output in some place I just like to put the suspend because usually you end up wanting it to spend at some point so that but that's that's a different thing so how would you use context receivers for to do dependence injection of your logger interface so the first thing you do is you add this to the signature or of the function where you are using so we have this download data function and at some point we will like to lock things uh because it's doing whatever so we add this context logger uh be yeah before the whole thing you can act this I put it on top to like showcase it's a context but it's it's like a very long uh signature so it's actually like context loggers to spend fun it's all like one big thing so that's the first step and then magically the interface so the logger is available in the body of the function as you can see here I'm calling log and this log is not coming from any extension because there is no extension receiver is not coming from the surrounding class because there is no surrounding class is actually coming from the context is something I'm calling from blogger and I will shortly go later on on how it actually works that that you can actually call log inside of it but that's the idea whatever it's in the context is now available in your body and the nice thing is that now you can uh you can call other functions which use the logger so in this initialized app I'm using my download data which as I showed uses the logger let's assume there is also like a download Avatar which also uses the logger and as you can see I just declared that my initialize application uses the logger and then nothing else so any so if I'm not using the logger at all this is completely hidden except for this one point which is the signature of my function where I declare this should be using the logger the rest is kind of Hidden Away and the last part is at some point you need to provide this logo right I mean you until now you are declaring that you depend on this interface but you can use this uh with function and the fact that it's a function is is something quite interesting we will delve into that later but with this function what you do is you bring this console logger into a scope and within the block now you have whatever was on the scope plus a logger which means that now you can call initialize up because the logger is uh let's say it's in the context uh if you didn't have the width by the way you will have an error saying there is a missing logger in the context so I cannot call this function which I also find uh like a nice error right it's like telling you exactly what you are missing so that's those are the pieces uh to use context receivers so you declare them in the function if you have in the in the body you use it as it is part of the con of their scope then if you are not using it you don't see it except for in the in the signature and then you use width to inject the thing usually like at the top level of your application or whatever so this point is maybe interesting and except especially because we are in advance because the conference can to figure out what is this when we are using contact receiver because this gets uh a bit hairy so in binding we have this uh function so we have this uh we are defining it inside a database class it has a context and we have this function which is an extension on user but also has a logger in the context so that means that in order to use this function we need to have we need to be in the context of a database analog and have a logger in the context and then we have to call Save on a user so we need all of these things to be present so this corresponds to the three different kinds of receiver that's how they call it in in the documentation so uh the the fact that you are inside the class is what they call the dispatch receiver so that's where you know that this has database then you have the context which you can have more than one by the way so this is also part of it and then you also have the extension receiver so maybe if you have done some weird stuff you may have come with an error with this and symbol that's that's how coding essentially represents that this has a special type which is the combination it's actually the intersection of of these three things it is a database this is a database and logger and user which means that when you write this dot you can actually call anything on database logger and user and hopefully nobody has the same method with the same name so everything can be resolved automatically and it's like everything works magically but what it's actually happening is that this has a special type and from this the compiler can actually tell what are the methods available there uh you can if you if you need you can actually refer to one specifically by having this this at type uh thing so this if I write this as logger that means I want to only see like the the logger facet of the of this so it's in this case it will mean I'm accessing the the con the logger in the content if I write this is a database I'm going to the dispatcher and so on cool so so that's just to to remind you yeah so we have all this thing dispatch receiver that's well the method is declare extension when you use type uh like type dot the current function that means then you can call it uh with Dot and then you have context receiver which is the new thing it is a bit like uh extensions on asteroids because you can you have more things coming inside what you lose is that you cannot use the dot you need to explicitly uh call the values with width so you need to have with blah blah blah and then you can call the function whereas with an extension you can just use the thing dot method cool so what can we do with this already mentioned a little bit about dependence injection so that's that's like the basic use and if you read anything about contact receiver that's what most people use it for so imagine we have here uh some logging code it doesn't really matter that much the main thing is that I'm logging the stuff here so you see the log there and I'm kind of doing HTTP post request whoa whoa I did this with the with this and then okay so you uh we are doing an HTTP pause request so that means that we have two dependencies in this code we have the logger but also we have some kind of HTTP service or whatever way we are doing this coming from the HTTP post so you can see there that one nice thing about contact receiver is that these two things compose you can actually say you have you need these two things in the context uh uh and then we can play a bit with these two basic rules so so the context declares all the services you require and with allows you to inject the values so just by following these two rules you you kind of can do everything the Penance injection can do but uh a nice more type save and I would say better somehow so the first thing is because I have the context being part of the signature uh that means that a lot of thing which usually happen maybe if you are using the Penance injection framework at runtime something will fail at runtime you have others like a coin which happen at compile time but it's still later in the process uh with this the compiler knows about this because it's part of the language which means you can check your usage uh hopefully the you know the jetbrain people will introduce things in the IDE to do this and what I think is nicer is that you are very explicit about what you actually need is part of the documentation of your funds this this becomes something that the team can can share can know directly on the docs what what's happening there uh another interesting thing is that uh you can actually build uh things which depend on other contexts so for example we can say that the particular implementation of our HTTP service which actually goes to the network depends on the logger because it's doing some login inside so you can declare this and actually there is a trick this context cannot is not only available on functions you can also declare this in a class and that essentially means that when you call the Constructor you need to provide the context and inside the class the context is also available so it's like for the entire class you need this thing you need this to build the class and this essentially what it's doing is imposing and ordering in which you have to inject your things because in order to create the network HTTP service you need a logger that means you first need to create the logger and then put it in the context and not only then you can call the width or you can sorry you can only call The Constructor of the network HTTP service so essentially this is like a compile time check for ordering so you you know when you end up having all these complicated graphs of the not hopefully not graphs like trees of dependencies uh these checks and compiles them that you are doing it correctly because otherwise their code doesn't compile uh and even better because this width is just regular quadrants and some other patterns are kind of easy to achieve too so for example imagine we are running some tests and we want to create a mock well in this way it's quite easy because you just Implement your service in some other way like I'm you know like a network service which always fails because I want to test the non-happy path of my program then you can do this and then you just say with always fails and then you run your test here yes regular calling you're injection what you want without any hassle you can also combine the fact that you can get the context and change and use with to change it a bit for example imagine that we at some part in our code we don't want to have so many uh so many login because you know somebody wrote the other library and included 10 000 million log logs uh things but we don't really care about that part so we want to say in this tiny block or big block we only we only care about the critical errors which are logged so what we can do is create a new logger which wraps the other logger that's what I'm doing here in the class and then I'm overwriting log in such a way that we say you know if it's info or warning I don't care just do nothing otherwise just call the underlying logger and you know yeah maybe it's only left is critical errors or whatever just imagine the idea but the good thing is now we can just use our regular logger for the important stuff and then for this part which we don't really care we only care about the critical things we use with we take the logger we have in the context wrap it with the only important logger and use with which means that in this part and not a different logger is being injected which only you know which overwrites and only that's what we actually care about so that's you know we are combining these two things to get a nice and nice pattern here and another thing we can easily do using this is we can also easily combine this kind of services into one single module and then you know uh just uh pass all of them at once uh by wrapping this in the model and we can actually use delegation to have nicer syntax so what we are saying here is that app mode is actually a logger and we are delegating to the logger field so it's like this implements the two things and you know you use di Frameworks you have sometimes this module thing which just combined Services because when you have 10 of them you don't want to create all of them I have cool nice so what's the next thing you can do you can actually so that's that's you know that's already great we don't need di Frameworks anymore there's something which goes built into the language so in the same way that we have to spend and we don't have to care about the flat maps in the Futures and all this kind of things I like having things in my language well but type classes so let me introduce the problem a bit uh so if if you've done any Java uh programming you may know about this comparable class I think it's the same in kotlin 2 but anyway uh is this interface in which you have this compared to and then you compare to object and then you get an integer and if it's greater than zero that means that the first one is greater than the other one and so on uh so we have this interface and allows us to write code like this now this actually is a bit problematic because you can only Implement interfaces if you actually control the code which implements the interface you cannot go there and just shoehorn another interface and and it has another problem what about comparing things in different ways now we can solve this with context receivers what we do is instead put the comparable as part of the context there so now it doesn't come sort of as part of the T it comes as an additional part of the context and now imagine that we are implementing a new serialization format and as I said nobody implemented for booleans or we want to override this in in some other way well we can now do this because we can say look here is how I want my pretty of Boolean to look like I just have these interfaces and as a different object and then using width I put it in the context so instead of being part of the Boolean class now it's part of a context that has to be provided in order to run this whole thing now you will of course say yeah but now I have to provide all this with with all this thing you know that's terrible and that's why languages which actually have this pattern in the language like Haskell and Scala have special syntax uh but the arrow developers and working on something where you just say provider and then it put it essentially writes the width for you uh so you don't have to do this so that's that's one of the things maybe in the future will be useful who knows anyway as I said uh this is one problem because now we can inject the pretty of Boolean even if we don't control Boolean which we don't because somebody wrote it in the in the standard library but what if why we want to sort and this is actually coming from from accordance under Library you want to sort the list but this has to be comparable but what if I want to sort backwards of using a different key we cannot do this because the comparable is one per type because it's extending the interface uh so what happens in the library is that you actually have a different function called sort with which takes a comparator which is how you want to compare things oh wow I don't know how I did that it's this is all very weird uh so it actually you have to duplicate the function but it actually the function looks pretty much as the interface I was showing before so actually if we instead use a context receive a contact receiver here that will be uh much nicer because you know if it's in the context we don't have to pass it around in our competitors but now we can Define things like this as this wrapper thing I mentioned like this reverse wrapper which takes a comparable and just create something which Compares it in the other direction so it takes some a comparable in the context T it has to exist and then it's just make it go the other way around and now if I want to compare icon to sort the list in reverse order I just inject a different comparable I say with reverse and that creates a new comparable which goes in the other direction oh it's wrong yeah is it is it wrong yeah maybe maybe it's the other one who knows this sword is the same sword but we are overriding the comparison here cool so that's that's great that's that's a way useful for executives as I said like comparable and uh serializable but let's go to the last crazy thing and I have to go through this because urset I have somebody has to use the word effects so I'm I I'm one of the people who will use them um so yeah in in common you have these two ways to handle errors right you have exceptions which are untyped uh you but it has good things if you are not handling them you have you write no code just think just manually bubble and it has a special syntax like try and catch so you you clearly show what's Happening now the other possibility is to use things like result or nullable types which are more explicit you declare them but uh you know then you have to write boilerplates maps and flat Maps or winds and things like this so what if we could have the best of both worlds right like exceptions so you if you are not handling them then you don't see them but they are still explicitly type does it ring a bell yeah we can do this with context receivers by having yeah that's actually part of of Arrow uh I don't know if it's gonna come I think it's already in the library or maybe in the new release it will come so we can put this thing on the context it's called race and that's like a special type who only Simon knows how it works so ask him uh uh uh so you put it anyway you put it on the context and it's like you are declaring you know this now can raise this error this is like a checked exception but if you are not using it if you are not catching the exception you don't see them so you can just say you know read all the files each read file can throw a file error thing but you know it's not there you just use your regular map so in that sense it's like an exception you don't see them if you don't handle them but it's it's nice in that you are explicitly typing this uh yeah so you know you can actually have your own error so these don't have to be exception which is also a nice thing you can have your own header hierarchy and then just say you know I'm gonna raise this special file error and you just instead of throw use raise which is the name we came up with because you can use throw because it's a keyword but that's that's kind of the idea and and again once you once you have this you can just kind of run it you use this effect thing which essentially will give you back like a nullable or an either or whatever type you want to get or a result back and then you can just handle them so if you are handling them then of course you have to be explicit about this because you are handling this now we actually have nice things called catch and so on so this is a bit older but it's it's getting better we are working on a nice API following this design idea anyway just and once you start seeing context you see them everywhere what about the lounge in the core routine scope like this is something in the library uh well this actually uses extension function but it's it we could also make them like a context function right because it doesn't have to be exactly the extension we just need like the capability of being able to launch new things uh so it's like keratinoscope is actually providing a service or an effect it's like declaring one extra thing you can do within your blog it's on a special service which in this case is creation of concurrent jobs so once you go this you start seeing effects everywhere you can say oh actually my read configuration file uh uses some kind of uh jobs on of some threat management so I need a routine scope in my context it may also fail so I need a raise file error in my context and by the way it reads the file system so I'm gonna also introduce a new file system service for this so once you start looking at this you it's like everybody everything just becomes things in the context and you can be very precise about this so before Simon just throws me out of the stage content receivers are awesome you can use this for everything I mean dependency technique is what they are thought for but as I said you can use this to provide extensibility in many different ways and also to have the nice the good things of exceptions with the good things of uh type errors in one go it's been a pleasure [Applause] [Music]
Info
Channel: Xebia
Views: 1,995
Rating: undefined out of 5
Keywords: Xebia, IT Consultancy, Agile, Quality & Test Automation, DevOps, Full Stack Development, Continuous Delivery, Big data, Deployment Automation
Id: 2oiRCYnqhDs
Channel Id: undefined
Length: 25min 15sec (1515 seconds)
Published: Thu Dec 01 2022
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.