官术网_书友最值得收藏!

  • Functional Kotlin
  • Mario Arias Rivu Chakraborty
  • 932字
  • 2021-06-24 19:15:26

First-class and higher-order functions

The most foundational concept of functional programming is first-class functions. A programming language with support for first-class functions will treat functions as any other type; such languages will allow you to use functions as variables, parameters, returns, generalization types, and so on. Speaking of parameters and returns, a function that uses or returns other functions is a higher-order function.

Kotlin has support for both concepts.

Let's try a simple function (in Kotlin's documentation this kind of function is named lambda):

val capitalize = { str: String -> str.capitalize() }

fun main(args: Array<String>) {
println(capitalize("hello world!"))
}

The capitalize lambda function is of type (String) -> String; in other words, capitalize will take String and return another String—in this case, a capitalized String.

As a lambda function, capitalize can be executed using parentheses with parameters (or no parameters at all, depending on the situation).

But what does the (String) -> String type mean?

(String) -> String is a shortcut (some could call it syntactic sugar) for Function1<String, String>Function1<P1, R> is an interface defined in the Kotlin standard library. Function1<P1, R> has a single method, invoke(P1): R, that is marked as an operator (we'll cover operators later).

Kotlin's compiler can translate the shortcut syntax into a fully fledged function object at compile time (indeed, the compiler will apply many more optimizations) as follows:

val capitalize = { str: String -> str.capitalize() }

It is equivalent to the following code:

val capitalize = object : Function1<String, String> {
override fun invoke(p1: String): String {
return p1.capitalize()
}
}

As you can see, the capitalize value's body is located inside the invoke method.

In Kotlin, lambda functions can be used as parameters in other functions as well.

Let's take a look at the following example:

fun transform(str:String, fn: (String) -> String): String {
return fn(str)
}

The transform(String, (String) -> String) function takes one String and applies a lambda function to it.

For all intents and purposes, we can generalize transform:

fun <T> transform(t: T, fn: (T) -> T): T {
return fn(t)
}

Using transform is very simple. Take a look at the following code snippet:

fun main(args: Array<String>) {
println(transform("kotlin", capitalize))
}

We can pass capitalize as a parameter directly, great stuff.

There are more ways to call the transform function. Let's try some more:  

fun reverse(str: String): String {
return str.reversed()
}

fun main(args: Array<String>) {
println(transform("kotlin", ::reverse))
}

reverse is a function; we can pass a reference to it using a double colon (::) as follows:

object MyUtils {
fun doNothing(str: String): String {
return str
}
}

fun main(args: Array<String>) {
println(transform("kotlin", MyUtils::doNothing))
}

doNothing is an object method, and in this case, we use :: after the MyUtils object name:

class Transformer {
fun upperCased(str: String): String {
return str.toUpperCase()
}

companion object {
fun lowerCased(str: String): String {
return str.toLowerCase()
}
}
}

fun main(args: Array<String>) {
val transformer = Transformer()

println(transform("kotlin", transformer::upperCased))

println(transform("kotlin", Transformer.Companion::lowerCased))
}

We can also pass references to instances or companion object methods. But probably the most common case is to pass a lambda directly:

fun main(args: Array<String>) {
println(transform("kotlin", { str -> str.substring(0..1) }))
}

There is a shorter version of this using the it implicit parameter as follows:

fun main(args: Array<String>) {
println(transform("kotlin", { it.substring(0..1) }))
}

it is an implicit parameter (you don't declare it explicitly) that can be used in lambdas with just one parameter.

Although it is tempting to use it for all cases, once you start using it with successive or nested lambdas, they can be difficult to read. Use it sparingly and when it is clear which type it is (no pun intended).

If a function receives a lambda as the last parameter, the lambda can be passed outside the parentheses:

fun main(args: Array<String>) {
println(transform("kotlin") { str -> str.substring(0..1) })
}

This feature opens up the possibility of creating Domain Specific Language (DSL) with Kotlin.

Do you know about the unless flow control statement from Ruby? unless is a control statement that executes a block of code if a condition is false; it's kind of a negated if condition but without an else clause.

Let's create a version for Kotlin by executing the following code snippet:

fun unless(condition: Boolean, block: () -> Unit){
if (!condition) block()
}

fun main(args: Array<String>) {
val securityCheck = false // some interesting code here

unless(securityCheck) {
println("You can't access this website")
}
}

unless receives a condition as a Boolean and blocks to execute as a lambda () -> Unit (no parameters and no return). When unless is executed, it looks exactly like any other Kotlin's control flow structure. 

Now, type alias can be mixed with functions and used to replace simple interfaces. Let's take the following example, our Machine<T> interface from Chapter 1, Kotlin – Data Types, Objects, and Classes:

interface Machine<T> {
fun process(product: T)
}

fun <T> useMachine(t: T, machine: Machine<T>) {
machine.process(t)
}

class PrintMachine<T> : Machine<T> {
override fun process(t: T) {
println(t)
}
}

fun main(args: Array<String>) {
useMachine(5, PrintMachine())

useMachine(5, object : Machine<Int> {
override fun process(t: Int) {
println(t)
}
})
}

It can be replaced with a type alias and used with all the function's syntactical features:

typealias Machine<T> = (T) -> Unit

fun <T> useMachine(t: T, machine: Machine<T>) {
machine(t)
}

class PrintMachine<T>: Machine<T> {
override fun invoke(p1: T) {
println(p1)
}
}

fun main(args: Array<String>) {
useMachine(5, PrintMachine())

useMachine(5, ::println)

useMachine(5) { i ->
println(i)
}
}
主站蜘蛛池模板: 广昌县| 高雄县| 利辛县| 天峨县| 红桥区| 兴隆县| 布尔津县| 嘉兴市| 南城县| 黎川县| 湘潭县| 温宿县| 女性| 东方市| 民和| 揭阳市| 石河子市| 枣庄市| 岳普湖县| 和龙市| 杭州市| 广丰县| 海宁市| 浏阳市| 句容市| 巩义市| 奎屯市| 柳河县| 临沂市| 五华县| 大邑县| 绿春县| 共和县| 平定县| 德惠市| 泰兴市| 龙江县| 鄂州市| 平舆县| 安龙县| 会东县|