The Programmers Guide To Kotlin - Anonymous and Lambda Functions
Written by Mike James   
Monday, 29 October 2018
Article Index
The Programmers Guide To Kotlin - Anonymous and Lambda Functions
Lambda Functions
Closure

Lambda Functions

The usual reason for introducing lambda functions into a language is to allow functions to be treated as more like first class citizens in the language. In the case of Kotlin we already have the ability to store and pass references to both named and anonymous functions.

In this case lambdas mostly provide a more compact way to define functions.

The key difference between a named or anonymous function and a lambda function is that a lambda is treated as an expression which can be invoked using the () operator.

A lambda function can be declared using the arrow operator -> within curly brackets. On the left of the arrow you list the parameters, and on the right you list the code that works with the parameters.

Notice that you cannot specify a return type for a lambda – it has to be deduced by type inference.

That is a lambda is:

{ parameter list -> code }

The code can either be a single expression or a multi-line body and the last value computed is returned as the value of the function.

For example, we could write the anonymous sum function introduced in the previous section as:

var sum={a:Int,b:Int -> a+b}

where we are relying on type inference for the type of sum and hence the return type.

You can call sum in the usual way:

sum(1,2)

However, lambdas are usually passed as parameters to other functions and tend to be defined as needed rather than stored.

Lambdas & Anonymous Functions As Extensions

You can define both lambda and anonymous functions as extensions and they work in the same way.

For example:

val sum=fun Int.(other:Int):Int {return this+other}

defines an anonymous function that is an extension method of Int called using:

1.sum(2)

If you want to use a lambda in this way you have to supply the type:

val sum: Int.(Int)->Int= {other:Int-> this+other}

and it can be called in exactly the same way:

1.sum(2)

You can pass a lambda extension in the same way, by specifying a function type:

fun myEvaluate(f:Int.(Int)->Int):Int{
        return 1.f(2)
}

Now you can call myEvaluate with a lambda-like syntax:

myEvaluate({other:Int->this+other})

or more simply:

myEvaluate {other:Int->this+other}

As the documentation explains, this can be used to create type-safe builders.

DSLs & Metaprogramming

final version in book

No Local Return

A surprising restriction is that you can’t use return in a lambda function. A return always returns from a function defined using fun and this is something that is enforced by the JVM. Lambda functions in Kotlin are not implemented in this way and hence you cannot use return.

You can, however, use a qualified return which behaves more like a goto command. The problem with a qualified return is that, because a lambda definition is so short, there isn't a sensible location to label.

The best you can do is something like:

var max = lambda@{ a: Int, b: Int ->
         if (a > b) return@lambda a
         return@lambda b
    }

which works but isn't a particularly clear expression of what is happening. Notice that this function is much better written as:

var max:Int =  {a: Int, b: Int ->
         if (a > b)  a else b
    }

where the if is interpreted as an if expression returning either a or b.

It is worth knowing at this early stage that you can use a return in an inlined lambda function, see later.

Compact Lambdas, it, _ & IIFE

One of the aspects of lambdas that can be confusing is how compact they can be. For example if there are no parameters you can simply omit them. So:

{1+2}

is a lambda that returns the result of 1+2, i.e. 3

The very simplest lambda is just a single value which by default is its return value:

{0}

which is a lambda that returns 0.

If your lambda has a single parameter, you also don't need to explicitly provide it in the definition, as the compiler will assume that any use of a variable named it in in the body is a single parameter.

For example:

var d={it*2}

is a single parameter lambda that returns the parameter times two and you would call it using;

println(d(2)) 

You can also leave out a parameter in a lambda if it isn't going to be used by replacing it with an underscore. This is useful when you have another function that calls the lambda with more parameters than you care to use.

For example the arith type demands that any lambda that conforms has two int parameters:

typealias arith =(Int,Int)->Int

If we want to write an arith lambda that only uses the second parameter we can write:

var neg:arith={_,b->-b}

this still conforms to the arith type, even though it only uses one parameter. Of course, you could simply include a dummy parameter and then ignore it, but this form at least makes it clear that you are only using a single parameter.

Notice that these expressions are functions and to return a value they have to be invoked. You can call a lambda immediately after it has been defined – a so called Immediately-Invoked Function Expression or IIFE.

For example:

var sum={1+2}()

sets sum not to a function but to 3. Similarly:

 var max:Int =  {a: Int, b: Int ->
         if (a > b)  a else b
    }(1,2)
println(max)

prints 2 and you do have to supply the type of max as the compiler can't work it out for you.

You can IIFE any lambda function, but you cannot IIFE a named or anonymous function because they are not expressions.

<ASIN:1871962536>



Last Updated ( Saturday, 03 November 2018 )