DEV Community

Praveen Kajla
Praveen Kajla

Posted on • Updated on

Demystifying Advanced Kotlin Concepts Pt.1

Ever since Google made it official for Android I had a constant urge to learn kotlin. The basics were easy to grasp as I come from the java background, but I was not familiar with the functional programming paradigm. So I stopped learning kotlin after the basics.
Then I learnt functional programming in javascript which was like a gust of wind and it hit me off that concepts are same like HOC, arrow function, pure functions just the syntax is different.This post assumes you know some functional programming principles but don't worry in case you don't. You can watch this awesome introduction.
Enough now, let's board this ship to kotlin.

Functions

01. Local Functions

fun OuterFunction(param:Int){
  val outerVar = 11
  fun localFunction(){
    println(params)
    println(outerVar)
  }
}
Enter fullscreen mode Exit fullscreen mode

Local functions are functions inside a function. Local functions could access outer function's parameters & its local variables (i.e. the closure).

What's the use

This is useful when we want to do code reuse i.e. we don't want to create a top level function or we don't want
to create a member function outside of the class.This way grouping is better.

Note
For other function outside of outerFunction we don't have access to outerFunction's localFunctions .

02. Infix Functions

for extension functions or member functions that have single parameters we can call these using infix notation

infix fun String.extensionFunction(x: Int): Int {
...
}
//can be called as 

val str = "hello"
str extensionFunction 2
Enter fullscreen mode Exit fullscreen mode

useful in UNIT TESTING

03. Anonymous Functions

We have seen higher-order functions where lambda expressions are passed as code blocks.

So Anonymous Functions are slightly different .

fun op(x:Int,op:(Int) -> Int):Int{
  return op(x)
}

Enter fullscreen mode Exit fullscreen mode

lambdas

//called like

op(3,{it*it})
//here {it*it} is lambda expression
Enter fullscreen mode Exit fullscreen mode

Anonymous

//can have multiple returns

op(3,fun(x):Int{
         if(x>10) return 0
         else return x*x
        })
Enter fullscreen mode Exit fullscreen mode

Anonymous functions have full body of a normal function but no name.

04.Inline Functions

A lambda expression in kotlin gives way to anonymous class in java.This adds its overhead and if lambda expression has a closure it would cause an instance to also be created thus adding more memory overhead.

In addition all these lambda expression are impacting the call stack.
It affects performance.

Using Inline Functions we can minimize these impacts.

NonInline


fun op(op:()->Unit){
    println("This is before lambda")
    op()
    println("this is after lambda")
}

fun main(args: Array<String>) {
    op({println("this is the actual function")})
}

Enter fullscreen mode Exit fullscreen mode

you can see decompiled kotlin bytecode here
noninline

Inline


inline fun op(op:()->Unit){
    println("This is before lambda")
    op()
    println("this is after lambda")
}

fun main(args: Array<String>) {
    op({println("this is the actual function")})
}

Enter fullscreen mode Exit fullscreen mode

you can see decompiled kotlin bytecode here
inline

if you compare the two decompiled versions you can see that what inline modifier does is

copy the entire inline function code to where we are invoking that function and it inline(copy) the code of the lambda being passed too

Inline takes a higher order function and inline it(copy and paste contents of that function) being passed in to where it is being called.

So, essentially it's flattening it out, it's saying instead of me invoking this function I'm gonna take the code and I'm gonna paste it there.

And this provides us with optimization because we are eliminating all of that unnecessary anonymous classes or call stack etc.

Of course, it comes with it's own side effects since we are copying and pasting code.
So inline modifier is obviously useful when I'm passing in the lambdas because otherwise it makes no sense.

Note
we can't actually store reference to that lambda function if want to inline it.


inline fun op(op:()->Unit){
    val reference = op //this would say illegal usage
    op()
    println("this is after lambda")
}

fun main(args: Array<String>) {
    op({println("this is the actual function")})
}

Enter fullscreen mode Exit fullscreen mode

If you more than one lambdas passed in as parameters to higher order functions you can specifically tell the compiler to not inline specific lambda function by using noinline modifier in front.

If body of function is large you may don't wanna inline it.

05. Returns And Local Returns

Let's take an example

fun ContainingFunction(){
  val nums=1..100
  numbers.forEach{
    if(it%5==0){
      return
      }
    }
  println("Hello!")
  }
Enter fullscreen mode Exit fullscreen mode

If you run this you would get an empty output i.e. no Hello! printed.
Because the input parameter to forEach function is a lambda expression and
return statement make us return from the containingFuction itself rather than the lambda expression.

So how to retun from lambda rather than containing function

Seems kotln has a way i.e. change the return statement

labels

...
  if(it%5==0){
    return@forEach //i.e. name of the higher order function
  }
...
//or you could define your own labels
numbers.forEach myLabel@{
          if(it%5==0){
            return@myLabel  
         }
   ....

Enter fullscreen mode Exit fullscreen mode

Note
But instead of lambda expression if we use anonymous function as parameter it would return from the anounymous function itself without
any labels

Also non local returns are only allowed in the case where they are invoked from an inline function.
what i means is if we look kotlin's forEach code you can see that it has inline modifer in it

So if we remove inline modifier from forEach it would show error.

06. Lambda Extensions

or lambda receivers

//lambdas
f:() -> Unit
//lambda extension
f:SomeFunctionOrClass.() -> Unit
Enter fullscreen mode Exit fullscreen mode

Take time to look at this sample resthandler

we can see that routeHandler has lambda extension as parameter

fun routeHandler(path:String,f:RouteHandler.() -> Unit):RouteHandler.() -> Unit = f
Enter fullscreen mode Exit fullscreen mode

So all extension functions have access to members of the class they are extending i.e. routeHandler has excess to the request and response members of the the RouteHandler class

This Helps us in creating a very fluent ,expressive DSLs
further example

06. Invoking Instances in Kotlin

or lambda receivers

... main{
...
  val manager = Manager()
  ...
 }
 class Manager{

 }
Enter fullscreen mode Exit fullscreen mode

Now Imagine a scenario in which I want to invoke some functionality of Manager class Just by using the instance
like

manager("do Something")
Enter fullscreen mode Exit fullscreen mode

We can do that with kotlin

We can simply implement a function called invoke which is defined as an operator and which can take any parameter

class Manager{
  operator fun invoke(value:String){
    prinln(value)
   }
}
Enter fullscreen mode Exit fullscreen mode

Next Part: Classes

Top comments (5)

Collapse
 
adrian110288 profile image
Adrian

Hey Praveen, loved the article. More of these please!

Collapse
 
praveenkajla profile image
Praveen Kajla

Thanks Adrian. Glad that you like the article.
Do checkout the next part

Collapse
 
thedeemon_lj profile image
Dmitry Popov

"woule", "Retuens", "Exrensions"

I understand it must be hard to feel the difference between "advance" and "advanced", "learned" and "learnt" and so on, but please at least use a spell checker.

Collapse
 
praveenkajla profile image
Praveen Kajla

Thanks Dmitry.

Collapse
 
kondenko profile image
Vladimir

This font is bad, try using something less fancy (like Roboto).