 Hi everyone, thank you for coming Yeah, so I'm happy to introduce Suraj is going to talk about dissecting the inline keyword in Kotlin. So please welcome you. Thank you. Hi So I'm Suraj I'm gonna be talking about dissecting the inline keyword in Kotlin Before I begin a bit about myself. So I I'm an Android developer I write I've contributed to Firefox the Mongo stitch SDK and the real machine on client as well I Was writing job before then I switched to Kotlin and now I'm actually exploring Golang a bit. I Did Android and now I've moved to back in mostly I am pretty active on Twitter and you can email me questions about slides Etc. Etc. If you have I Work at a company called Qtalk. It's mostly it's a company about synchronous Communication making it fun like you can do play games and browse the web together all over a call It is 100% Kotlin client and back in code and we also the default dialogue on Android So it's pretty heavy engineering that goes on to and all of it is in Kotlin There are some links to it Cool so inline keyword When we are actually writing a lambda or something in Kotlin the IDE mostly prompts us that Try using an inline keyword for it. But why do we actually use it? Like why do we need it? Why can't the compiler actually do it like automatically for us? So basically the meaning of inline actually goes for like placing things or arranging them in a line or doing things just like Like in a line But if it's that simple then why why does Kotlin need an actual a keyword for it? Like why can't the compiler automatically do it for you? There are multiple Aspects over here if you can drill down mostly into the version of Java that actually plays underlining Then we can actually dig deeper into it. So Java version compatibility Let's start with lambdas Lambdas as we all know higher order functions or anonymous functions. They were introduced in Java from Java 8 They are mostly implemented in Java using functional interfaces and they leverage on a specific bytecode instruction That was that was introduced in Java 7 called as invoke dynamic. It's a it's a pretty special bytecode instruction, which does a lot of things around it but after like since it was introduced in Java 7 like it and lambdas are pretty important in Constructing programming We lose a lot of stuff if we just Like try targeting it to Java 8 so we it's better like in order to have it working for like Java version 6 Etc. We need to have tools like retro lambda etc. Etc. Now the question is if we have tools like retro lambda and all of these components around it Why actually take up the effort like why does it how does it even affect Kotlin? So the ideology of around Kotlin is that it tries to do more things natively rather than using more number of libraries or tools around it and Why should we care since it's Java 6 and we have Java 14 coming and a lot of things that have changed our world Like why why even care about Java 6? The problem over here is that it's Android So Android and Java 6 are like Android is pretty much holding back the entire Java ecosystem all together because Java 8 and Android 7.1 which is Our Android 7 actually was the first version of Android which started into which introduced Java 8 So yeah, we should care because of Java 6 and Android mostly So that is the whole need of the and of the inline keyword is because you can't actually have it working Below Java 7 below Java 8 7 and 8 actually Yeah So let's start with the why how so how does actually Kotlin achieve it since we if we know that Kotlin compiles to Java bytecode and if the bytecode instruction itself is not present then how would how would Kotlin actually achieve it? So before we move there like Let's once again dive into lambdas and closures and a few concepts around them So Kotlin lambdas are actually different than the lambdas which you have in Java Kotlin lambdas are Mostly implemented using function types So you actually have a class called functions dot kt which has function like which has interfaces from function 0 to function 32 Based on the number of parameters arguments you have in a function like function 0 having 0 arguments and function 32 having 32 arguments There's actually an interesting keep for Expanding it to function 255 which is a maximum number of arguments you can have in Java And the reason why Java Java does it Kotlin does it this way is because In Kotlin functions are first-class citizens like Like null pointer exception is a first-class null null null ability is a first-class citizen in Kotlin similarly functions are also first-class citizen in Kotlin meaning that You you can actually assign a video again assign a function to a variable invoke a functions object later on Pass a function to a function etc. Etc. And that's the reason why you do it this way Closures closures on the other hand, it's more of a programming concept. It's like It's more related to a lambda anything which is outside the scope of a lambda and if it's consumed inside captured inside a lambda Then you call it a closure That's just basic concepts which you're going to use further down the slide So it's just good to have it clear all of this if you just see it programmatically once I have the code is legible So if you if you consider this example, we're going to use this example throughout So we have a function called as test lambdas which has a lambda my lambda and we have this This clue. Sorry. I'm so sorry and We have this my big loop, which is actually the Implementation of that lambda which consumes a lambda and you have this value value enclosure Which actually does nothing but just prints the value inside the lambda which is test lambda and the lambda value So this is lambdas and closures We'll move on to the next concept which is call sites So call sites again are mostly a concept which is related to lambdas and it's basically where you actually call a call site So mostly when we actually debugging on the Java application on to Java application on to Kotlin applications using ID like Andro studio or IntelliJ. We actually find an option for lambda function, which is if you press like jump to jump to source We actually find two options, which is Let's check the source or check the call site So call site is basically where you actually call the function There's a bug in this. There's a there's a mistake in this code is that there's no lambdas actually but still yeah So if if print me was a lambda and it was being consumed then I am group would be the calling site for that lambda Non-inland lambdas before we actually See how lambdas are and what do they do it would be a good point of reference to actually know What how non-inland lambdas would look like because there is a lot of unoptimization Like we'll actually get to see the difference between on inland lambdas and inline lambdas so We'll consider the same example as before we have a test lambda which is our lambda function We have our my big loop which is consuming the lambda and it's gonna iterate Like we have an int range which is 0 to 50 both inclusive and it's gonna run a loop and it's gonna call the lambda for the Number of times the loop is gonna run So this is the and the logical logic part of it, which is the test lambda's function Which is a lambda being called and printing a particular variable This is essentially the logical part of it now Let's let's take a step ahead and I try to try decompiling this code source code and Assembling it into Java again So if we do that we see something like this There's a huge block of code which is a lot of compiler injected code which we actually don't care about But the but the interesting concept over here is that the test lambdas are function was actually compiled into a class Which actually implements a function one type interface and we know function one because it has one argument So if hence is function one if it adds zero arguments, it would be function zero and it extends a lambda So your method having a lambda actually is compiled into a class now class extending lambda and implementing the function one interface and The logic for that particular lambda inside the invoke which is just printing a particular variable And if you see the call site, this is where the actual problem begins The call site looks something like this again, it's a lot of compiler injected code, which we do not care for much If you look at the lambda Like the lambda method being invoked specifically you see the test lambda function called along with the lambda which is actually and a new instance of a particular class we just saw The problem is here with the new instance is that it will create a new instance for every time the iteration or for every time the the iteration will run like and It has been converted into an interval because of in-range interest compiles to and iterable in Java So that is a problem. So you'll actually have 50 instances or 51 inches technically of The inline function that is going to happen. It's pretty bad And so this is the worst case scenario, but Kotlin compiler is a bit smart It also has a semi optimized scenario wherein if you don't have a closure It optimizes some things because if you don't have a closure It does not have to create a stack for you for every method in order to capture the variable that you have for that So for non inline for non inline lambdas without closure You don't actually have to care about much because there'll just be a single ten instance of that particular lambda If we take the similar example as we've seen before except for the closure you can see that there's no closure They just print lm. You're just consuming the value of the lambda within the lambda And if you try decompiling the source code you see that and you just print the call side because the lambda won't actually change It's only the culture call side that actually changes and You can see there is you don't have a new instance of it running every time You just have a single ten instance being created. It's still a bad thing because functions are technically Native or technically more of like a very strong construct inside Java. So like it would not be good if we have An instance of it every time even though even though it's a single ten instance because if you have n number of lambdas You'll actually have n number of instances, which you wouldn't have otherwise Also, it's not the way how Java does Java uses the invoke dynamic keyboard which bytecode which Kotlin does not No maintain compatibility. So this is how this is the worst case second This is semi-optimized scenario where you don't have the new instance created every time now. How about inline lambdas? How how do they look when you decompile them? Let's take a similar example from before where you have a test lambda and you lambda and the And the loop which is consuming it the same number of iterations all all we do now is just modified by adding one particular keyboard which is inline and Just again just put it inside the decompiler and just throw out Let's try printing the Java code out of it and if we see the code this time is different You don't have this is this is all that you get from it. You have no classes. You have no Instances you have no implementation of functional interfaces. No class which extends a lambda nothing This is the only thing which you get you have two static functions the test lambda and the my big loop Which you actually had in sour as well now here is the fun part which begins is that if you You see inside the my big loop function, you see the value in closures actually accepting the so this was a worst case scenario It is actually having the closure as well inside the lambda It has a loop as well, but it has no instances nothing. It's just like compiler copy paste is just everything is inline it has taken the logical block from your lambda taken it out and Printed into your call site This is the actual power of inline and it's magical in many ways because It not this infrastructure forms the basis of many things in Kotlin. It forms the basis for sequences Street, which is which are equivalent to streams in Java. It forms the basis for You have for each and all of these functions and you have in fact anything which leverages on the lambda architecture actually uses in line Internally you have code routines also because of their entire syntax you have keywords like apply etc. Etc. So It is it Just doesn't stop there. You have other aspects as well. For example one of the Like the real power over here is type reification So if we if we were to define how the reified keyboard or the entire reification mechanism It's actually it means making something concrete or having something survive Through a lot of stages. So and the context for it definitely is generics So if you if you take Java for example, how many of you over here know Java type erasure type erasure concepts in Java Okay, so theoretically what it happened. What happens is that? Anything inside angle bracket once it's passed into the compiler the you don't have the instance of it at runtime It's just Java like the runtime does not know what type of instance it is So consider this example over here You have a list of string which is actually an arrow list which is an arrow which is an instance of an arrow list And if you try printing the instance of that arrow list You like if you try running this code you actually get an error Saying it's an illegal generic type for instance off because the compiler because the runtime does not actually know and the compiler throws you a warning for that because a runtime does not know what what type of list what type of instance it is because Even though list of even the string is a type of is an instance of object list of string is not an instance of list of object But if you try changing list of object to list of string or to just list it works Because you it's still a list. You don't know what list it is, but it's still a list So this is essentially the type erasure concept in Java. Now. How does it apply at? For Kotlin and can inline help over you So let's let's take a more Kotlin example over here and see how things work So consider this example you have you have a generic function with Corrifi test and you are printing the type of the class and you're matching if it's a string or not in Very crude way How many of you think this code would work actually? like any ideas Like this would work it doesn't because it again throws this to same error because Java and Kotlin both have the same problem It says that you can't actually it's not a concrete type. It's not a very fight type Use a class instance. So we've seen many APIs and we've seen many Libraries use class with a capital C just so that they can actually do these type of operations on them now What if we do something like this we add two parameters to it, which is inline and very fight And that's it. It just works and If you see the internals for them, how do they work? Like this is our actual function in line very fight along with all the parameters in them And if you decompile them it decompile that particular source code and we see this particular Function the test function at with at the where the reified test function is actually called Sorry You see that it's actually comparing string and string. It's not doing anything It has actually taken everything from the logical piece of code and I just it's just checking string actually string to string So it's going to return true for you over there Which you which is actual desired result and you still have the lambda function available Which just checks for an object and a string and it's going to return you false because it's just f to maintain Compatibility and to maintain the presence of the function so you don't get method not found exceptions so This is how reified works and The original logic block always remains the same, which is what you are checking over you It just doesn't stop there you are the concept of in line over your which is applied to functions actually is extended to classes as well Which again essentially means that you can in line Like you can make things work during the compile time make make the compiler do some heavy lifting for you so let's consider like The in-depth annotation any Android developers around So you have you like Android developers might have seen the in-depth annotation in the Android source code So actually what it does is that? It's a type of a it's a way to Restrict your type which is only present in the compile stage because there's an there's an annotation processor Which just removes it post compile and you just have it During the working stage just to restrict your types So the idea over here is very is An amazing idea because you just want something at the compile stage to restrict types But and then they're essentially primitives half of the things that we do around these things are primitives so That's how in line classes were born actually so you have compiled them restrictions and objects for example This is a very bad example, but Yeah So you can actually take this as an example and we can have let's say an in-line class, which is actually a boolean boolean parameter which says that can a candidate have a seat or not and That's it It's essentially used to restrict types It has a couple of advantages and disadvantages. Let's discuss both of them as we go So the advantage is that it opens up an area of possibilities again to have things which Java could never have Like for example, you have unsigned integers unsigned long unsigned bite unsigned All the numerical parameters over here in Kotlin which I could not could never have what it does over here Is that it essentially just takes it? Does some compiler tricks over you on it and essentially just prints out an integer variable itself because it's still an end Or it's still a long for an unsigned log It makes it opens up a possibility and also It's true that Inline classes make great database IDs because you have let's say cat and You have a you haven't you have a database of zoos or animals or something like that Every database ID itself is a long but you have different IDs for different things So you in order to Since a long itself, but you want to restrict the type of long you can actually use inline classes to register the type of long There's actually a very good block push on them on this saying that inline classes make great database IDs It reduces the the need to create wrappers around primitives You can always create custom objects like I can create a customer instead of using the in def annotation I can actually create a custom custom object itself and have an integer inside it but having wrapper classes around primitives are not the best way because you are actually Creating more instances. It's not it's not a good memory practice to do the same thing So it reduces the need to have wrappers around primitives These are one of these are some of the great advantages of inline classes, but there are some disadvantages as well The biggest disadvantage is that it has mangling. Why does it have mangling is because Consider this inline class, which is the unsend integer class. It's actually taken from the Kotlin documentation online You take this function which has which is a compute function which takes an integer and you take this function Which is a compute function which takes an unsend integer both of them when when they'll be compiled they'll be they'll be the same function which is void compute and Integer so now since you have conflict over here for the naming the compiler appends Kind of a hash code we have behind the functions which use Inline classes you can actually see them once you see the compile code for them I actually forgot to print it over here. This is my mistake The second disadvantage within line classes is that it still has an experimental status It is not mature yet for no reason why and it sounds very similar to type alias It's not actually type alias because type alias does not introduce a new type It just they both are used for type restrictions But type alias does not introduce a new type whereas inline classes introduce a new type So suppose you create new inline class for something. Let's say a database ID for cats Then that would actually be a type during compile stage post-compiler. It won't But during compilers it is so the actual advantage of inline classes kick in when you actually use primitives It's otherwise. It's just the same You have the same there are certain advanced concepts in inline as well One would be cross inline. They just control the degree of inline You like the inlining of the lambda that the compiler that you want the compiler to do Cross inline is actually pretty interesting because It restricts non-local returns by non-local returns. We mean you actually block Lambda from returning the parent function and the one which is not cross inline does not block the same Let's let's look at this example. So over here the cross inline Keyword is present on the non-local return lambda, which is actually lambda function and you have a normal lambda as well And if we and this is how the call site might look like So if you look at it carefully The non-local lambda return has two returns One is that it returns the lambda itself this operation is permitted because you're still returning locally Which is you're still returning inside the scope of the lambda You're not actually trying to return outside the like outside the scope of the lambda Which is the function which is calling that particular lambda, but this would be illegal This is what cross inline actually restricts. It restricts non-local returns. That is it restricts returns from the same Returns from the lambda which are not like which is not like returns to the lambda which are its calling function in a way and Theoretically both of them would be possible inside the normal lambda function There's another point to it, which is no no inline no inline as the name suggests is Does not inline the code base for you does not inline the lambda for you If you ask what might be the reason to do this The reason is simple you since Kotlin in since in Kotlin functions are first-class citizens You might actually pass a lambda to a function to a system API or to some function Which is not under your control and if that function might not have been inline then there is no way for the compiler to actually inline it in simple words Like if you are passing a lambda to a function, which is not inline Then there's no way for the compiler to actually inline it. So that's what that's when you specify no inline That's when the compiler will not do the optimizations for you in terms of memory and CPU It will create more number of instances if you have a closure or it will create a static instance if you do not have a closure And if you if we consider an example over here This is our no inline lambda to This is a function which is not inline which actually takes a parameter as a lambda This would be illegal this function would be illegal if lambda to was not no inline Because again as we say as we discussed the compiler has no way for you to actually inline it. That's it There's there are a couple of links that are useful to this particular talk There's a blog written on this. It's still it's incomplete So there's a link to it. There's a good read on inline functions in general invoke dynamic It's a beautiful keyboard that was introduced in Java 7 it does a lot of heavy lifting and for Java as an ecosystem from Java 8 and Inline glasses which make great database IDs. It's a blog post by Jake Wharton who is people non-android people might not be knowing Jake Wharton and The app to my the link to the QTalk app the place where I work I have a couple of stickers as well in case you guys want to collect and the link to this presentation This presentation was actually called power of inline and the title just changed overnight. I Might be a bit fast. I'm sorry for this but yeah Are there any questions about? Lambdas Java inlining that I can I have ten minutes Any questions anything any questions guys? I'm done