Functional programming(FP) is based on a simple premise with far-reaching implications: we construct our programs using only pure functions—in other words, functions with no side effects.
What are side effects? A function has a side effect if it does something other than simply return a result. For example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// ASIS: A Kotlin program with side effects
class Cafe {
fun buyCoffee(cc: CreditCard): Coffee {
val cup = Coffee()
cc.charge(cup.price)
return cup
}
}
// INPROGRESS: Adding a Payments object
class Cafe {
fun buyCoffee(cc: CreditCard, p: Payments): Coffee {
val cup = Coffee()
p.charge(cc, cup.price)
return cup
}
}
1
2
3
4
5
6
7
// TOBE:
class Cafe {
fun buyCoffee(cc: CreditCard): Pair<Coffee, Charge> {
val cup = Coffee()
return Pair(cup, Charge(cc, cup.price))
}
}
creating a charge from the processing or interpretation of that charge. The buyCoffee function now returns a Charge as a value along with Coffee.1
2
3
4
5
6
7
8
9
// Data class declaration with a constructor and immutable fields
data class Charge(val cc: CreditCard, val amount: Float) {
fun combine(other: Charge) : Charge =
if ( cc == other.cc )
Charge(cc, amount + other.amount)
else throw Exception(
"Cannot combine charges to different cards"
)
}
Charge? It’s a data type we just invented, containing a CreditCard and an amount, equipped with a handy combine function for combining charges with the same CreditCard.Charge to be combined with another Charge instance.1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Cafe {
fun buyCoffee(cc: CreditCard) : Pair<Coffee, Charge> = TODO()
fun buyCoffees(
cc: CreditCard,
n: Int
): Pair<List<Coffee>, Charge> {
val purchases: List<Pair<Coffee, Charge>> = List(n) { buyCoffee(cc)}
val (coffees, charges) = purchases.unzip()
return Pair(
coffees,
charges.reduce { c1, c2 -> c1.combine(C2) }
)
}
}
The list is initialized using the List(n) { buyCoffee(cc) } syntax, where n describes the number of coffees and { buyCoffee(cc) } is a function that ini tializes each element of the list.
An unzip is then used to destructure the list of pairs into two separate lists, each representing one side of the Pair.
This is done by constructing a Pair of List<Coffee>s mapped to the combined Charges for all the Coffees in the list.