Saturday, 17 July 2021

Android Java to Kotlin migration by example part 10: higher-order functions

 A higher-order function is a function, which returns another function, or takes another function as parameter.

Today we'll begin with Kotlin code. Kotlin, being much more functional-oriented than Java (although Java is getting more functional since version 8), makes use of higher-order function much more elegant and readable.

Let's consider a mathematical function A, mapping some value to other value, for instance y=x*x (square). We can construct another function B (or functionAMultipliedBy2) which would map any x to functionA(x)*2.

In functional programming we can use a higher-order function in that case: create a function functionB, taking functionA as param. Such function can then be stored like a variable, and called. I guess it's more understandable in the code form. So let us take a look at a program defining our functionMultipliedBy2, getting a Float->Float function (getting float as param and returning a float) as parameter and returning Float->Float function (the function passed as a parameter multiplied by 2). 

Kotlin:

package com.mypackage

fun functionMultipliedBy2(
functionArg: (arg: Float) -> Float): (Float) -> Float
{
val funcToReturn = {a: Float ->functionArg(a)*2}
return funcToReturn
}

fun squareA(arg: Float): Float{
return arg*arg
}

fun main() {
println(squareA(2.0f)) // "4.0"

// we can pass squareA as parameter to functionMultiplied by 2
val squareDoubled = functionMultipliedBy2(::squareA)
// :: because it's class member

// squareDoubled is now a FUNCTION, taking Float
// as param and returning 2* square(param)

// we can simply call it:
println(squareDoubled(2.0f)) // "8.0"
}

The same code in Java would look like this:

import java.util.function.Function;

import static java.lang.System.out;

public class MainClass {

static Function<Float, Float> functionMultipliedBy2(
Function<Float, Float> functionArg)
{
Function<Float, Float> funcToReturn =
input -> functionArg.apply(input) * 2;
return funcToReturn;
}

static float squareA(float arg){
return arg*arg;
}

public static void main(String[] args){
out.println(squareA(2.0f)); // "4.0"

Function<Float, Float> squareDoubled =
functionMultipliedBy2(MainClass::squareA);
// -> ugly

// squareDoubled is an object of type Function
// getting Float as param and returning 2*square (param)

// call looks like this:
out.println(squareDoubled.apply(2.0f)); // "8.0"

}
}

The code execution result is the same in both cases and it's:

4.0
8.0

Please note how the Java code makes use of class Function objects to deal with functions. In Kotlin, we define returned value (or acccepted as argument) as function simply by (Float) -> Float notation.

What's more, in Kotlin we can simply call passed function. A Java Function object has to be used by calling .apply method to it (because it's an object, not a "real" function). I think Kotlin, as more functional-oriented languages, deals with this in much more "natural" way.

The higher-order function can also be used with lambda expressions. In Java:

import java.util.function.Function;

import static java.lang.System.out;

public class MainClass {

static Function<Float, Float> functionMultipliedBy2(
Function<Float, Float> functionArg)
{
Function<Float, Float> funcToReturn =
input -> functionArg.apply(input) * 2;
return funcToReturn;
}

public static void main(String[] args){
Function<Float, Float> squareRoot =
input -> (float)Math.sqrt((double)input);
Function<Float, Float> squareRootDoubled =
functionMultipliedBy2(squareRoot);
out.println(squareRootDoubled.apply(4.0f));
}
}

... as well as in Kotlin:

package com.mypackage

import kotlin.math.sqrt

fun functionMultipliedBy2(
functionArg: (arg: Float) -> Float): (Float) -> Float
{
val funcToReturn = {a: Float ->functionArg(a)*2}
return funcToReturn
}

fun main() {
val squareRoot = {arg:Float -> sqrt(arg.toDouble()).toFloat()}
val squareRootDoubled = functionMultipliedBy2(squareRoot)
println(squareRootDoubled(4.0f))
}

Again, Kotlin is much more concise - and allows us to simply call the function, without using any special .apply() methods.

No comments:

Post a Comment

Python crash course part 10: inheritance and polymorphism

In the last part we've shown how to create and use a class in Python. Today we're going to talk about inheritance: wchich means cre...