Define a Function
FILE : Part1DefineFunction.kt
In this module we are going to
- learn basic of Kotlin functions
- understand how they are implemented
- discover libraries which allow functional composition
Structure
If you are coming from Java the main surprise for you will be type moved to the right side of a variable. On the left side you will find all variable modifiers like var, val, final.
Now look at the type of a function
val f: (Int)->Int = ...
It actually consists of multiple types : two Int and an arrow. It shows which type function maps to which type. It may be as weel (User) -> Email or (Order) -> Price. For Scala Developers - argument type has to always be surrounded by parenthesis.
Now the right side of a function declaration
val f: (Int)->Int = {input -> input+1} //one parameter
val add: (Int,Int)->Int = {a1,a2 -> a1+a2} //two parameters
Function body is always wrapped in curly braces. You have again an arrow which divide arguments from actual body. Arguments doesn't have to be surounded by parentheses.
JVM Representation
Because Kotlin originally tegeted Java 6 for Android so currently each function is compiled to its own class.
javap target/classes/lodz/jug/kotlin/starter/fp/Part1DefineFunctionKt\$main\$f\$1.class
Compiled from "Part1DefineFunction.kt"
final class lodz.jug.kotlin.starter.fp.Part1DefineFunctionKt$main$f$1 extends kotlin.jvm.internal.Lambda implements kotlin.jvm.functions.Function1<java.lang.Integer, java.lang.Integer> {
public static final lodz.jug.kotlin.starter.fp.Part1DefineFunctionKt$main$f$1 INSTANCE;
public java.lang.Object invoke(java.lang.Object);
public final int invoke(int);
lodz.jug.kotlin.starter.fp.Part1DefineFunctionKt$main$f$1();
static {};
}
ls -al target/classes/lodz/jug/kotlin/starter/fp/
total 24
drwxrwxr-x 2 pawel pawel 4096 lis 13 19:14 .
drwxrwxr-x 4 pawel pawel 4096 lis 13 19:14 ..
-rw-rw-r-- 1 pawel pawel 1791 lis 13 19:14 Part1DefineFunctionKt.class
-rw-rw-r-- 1 pawel pawel 1345 lis 13 19:14 Part1DefineFunctionKt$main$add$1.class
-rw-rw-r-- 1 pawel pawel 1273 lis 13 19:14 Part1DefineFunctionKt$main$f$1.class
-rw-rw-r-- 1 pawel pawel 1275 lis 13 19:14 Part1DefineFunctionKt$main$f2$1.class
Composition
You may noticed that there are no method like andThen or compose in decompiled classes so you don't have composeability out of the box. Yet here another interesting mechanism from Kotlin enters the scene.
In Kotlin you can add behaviour to already defined types with extension methods. This mechanism is used by library :
https://github.com/MarioAriasC/funKTionale
<dependency>
<groupId>org.funktionale</groupId>
<artifactId>funktionale-all</artifactId>
<version>1.1</version>
</dependency>
now you can use andThen and other functions combinators.
val composed=parse andThen square
but you don't have to rely on external libraries. Kotlin allows you to add custom functionality you need through _extension methods _mechanism.
So normally if you would like to add some functionality to a function object you would have to pass actual function as a parameter and then parameterize its invocation
fun <A,B> genericInvoke(a:A,f:(A)->B):B = f(a)
Extension methods enters the stage. Through them you can add methods directly to a function.
fun <A,B> ((A)->B).customInvoke(a:A)= this(a)
fun createIncr() : (Int) -> Int = { it + 1}
val incr: (Int) -> Int =createIncr()
Displayer.section("generic invocation",genericInvoke(3,incr))
Displayer.section("extended function invocation",incr.customInvoke(3))
Later in exercises you will implement your own andThen function
Tuples
Tuple are a very convenient data structures when you want to return more than one element from a method/function. It may be a pair of key and data or maybe simple value plus metadata? In java you need to create Pair class each time you want to return more than one value from a function.
In Kotlin - well... - you also need to use Pair class but fortunately there is a lot of synthatic sugar to help you. First of all lack of new operator makes code more compact than in Java. Yet another thing is more important and it is only in Kotlin - destruction.
val pair=Pair(1,"a")
val (t1,t2) = pair
In the second line you can observe that we are able to assign tuple parts directly into variables. You need Three elements? Triple class is provided for you.
val triple=Triple(1,"a",false)
val (number,string,bool) = triple
Exercises
FILE : Part1DefineFunctionExercise
Level 1
- Complete functions definitions - replace all TODO with proper body
- Operations on tuples - implement two functions - one returns second element of a tuple, second swaps elements in tuple
- In third exercise you have three functions which you need to arrange in proper order to receive expected result
Level 2
- define custom andThen method
- it is defined at the bottom of the file
Level 3
- Implement generic andThen version
- Implement andThenSeq which compose list of a functions A->A into one functions.
Level 4
Implement custom class MyBox with proper implementation of high order functions map and check so that following is true :
box1.map {it +1 } .check { it== 2 } shouldBe true
box2.map { "prefix_" + it } .check { it== "prefix_word" } shouldBe true
box3.map { it.email }.check { it== "[email protected]"} shouldBe true