Function composition

One big part of functional programming as a concept is to use functions in the same way that we use any other type—as values, parameters, returns, and so on. One thing that we can do with other types is to take them as construction blocks to build other types; the same concept can be applied to functions.

Function composition is a technique to build functions using existing functions; similar to Unix pipes or channel pipelines, the result value of a function is used as a parameter for the next one.

In Arrow, function composition comes as a set of the infix extension functions:

Function

Description

compose

Takes the result of invoking the right-hand function as the parameter for the left-hand function.

forwardCompose

Takes the result of invoking the left-hand function as the parameter for the right-hand function.

andThen

Is an alias for forwardCompose.

 

Let's compose some functions:

import arrow.syntax.function.andThen
import arrow.syntax.function.compose
import arrow.syntax.function.forwardCompose
import java.util.*

val p: (String) -> String = { body -> "<p>$body</p>" }

val span: (String) -> String = { body -> "<span>$body</span>" }

val div: (String) -> String = { body -> "<div>$body</div>" }

val randomNames: () -> String = {
if (Random().nextInt() % 2 == 0) {
"foo"
} else {
"bar"
}
}

fun main(args: Array<String>) {
val divStrong: (String) -> String = div compose strong

val spanP: (String) -> String = p forwardCompose span

val randomStrong: () -> String = randomNames andThen strong

println(divStrong("Hello composition world!"))
println(spanP("Hello composition world!"))
println(randomStrong())
}

To build the divStrong: (String) -> String function, we compose div:(String) -> String and strong:(String) -> String. In other words, divStrong is equivalent to the following code snippet:

val divStrong: (String) -> String = { body -> "<div><strong>$body</div></strong>"}

For spanP:(String) -> String, we compose span:(String) -> (String) and p:(String) -> String as follows:

val spanP: (String) -> String = { body -> "<span><p>$body</p></span>"}

Notice that we are using the same type (String) -> String, but any function can be composed if it has the right return type that the other functions need.

Let's rewrite our Channel pipeline example with function composition:

data class Quote(val value: Double, val client: String, val item: String, val quantity: Int)

data class Bill(val value: Double, val client: String)

data class PickingOrder(val item: String, val quantity: Int)

fun calculatePrice(quote: Quote) = Bill(quote.value * quote.quantity, quote.client) to PickingOrder(quote.item, quote.quantity)

fun filterBills(billAndOrder: Pair<Bill, PickingOrder>): Pair<Bill, PickingOrder>? {
val (bill, _) = billAndOrder
return if (bill.value >= 100) {
billAndOrder
} else {
null
}
}

fun warehouse(order: PickingOrder) {
println("Processing order = $order")
}

fun accounting(bill: Bill) {
println("processing = $bill")
}

fun splitter(billAndOrder: Pair<Bill, PickingOrder>?) {
if (billAndOrder != null) {
warehouse(billAndOrder.second)
accounting(billAndOrder.first)
}
}

fun main(args: Array<String>) {
val salesSystem:(Quote) -> Unit = ::calculatePrice andThen ::filterBills forwardCompose ::splitter
salesSystem(Quote(20.0, "Foo", "Shoes", 1))
salesSystem(Quote(20.0, "Bar", "Shoes", 200))
salesSystem(Quote(2000.0, "Foo", "Motorbike", 1))
}

The salesSystem: (Quote) -> Unit function is very complex in its behavior, but was built using other functions as building blocks.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.225.57.126