Functional Programming with Kotlin and Arrow: Getting Started
In this tutorial, you will learn the fundamentals of functional programming and how various Kotlin language features enable functional programming concepts. By Massimo Carli.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Functional Programming with Kotlin and Arrow: Getting Started
30 mins
- Getting Started
- OOP in a Nutshell
- Types as Abstractions
- Abstracting Identity
- Collaboration
- Immutability
- Moving From Objects to Functions
- Defining Functions
- Function Types
- Applying Higher-Order Functions
- Functions in Kotlin: Considering Special Cases
- Special Case #1: A Function With Nothing
- Special Case #2: The Unit Function
- Special Case #3: Predicate Functions
- Function Composition
- Side Effects
- Pure Functions
- Logger: The FP Way
- Applying Abstraction
- Implementing Composition
- What About Arrow?
- Where to Go From Here?
Defining Functions
A function receives data as input and produces output, which may or may not depend on the input. Picture a coffee machine. The input is water and coffee beans, and the output is coffee:
A function can be something that receives a book
as input and returns its price. More importantly, a function is a way to map items of one type into items of another type. Kotlin isn’t an FP language, but it contains different features that allow you to use many FP concepts.
Next, create a Functions.kt file and add the function below, which can be used to return the weight of a Book
:
fun bookWeight(book: Book) = book.weight
In the same way you can create a function that returns the price of a Book
. Add this code to Functions.kt:
fun bookPrice(book: Book) = book.price.value
In Kotlin, functions are first-class citizens and can be treated as data. This means that you can define the previous functions like this:
val bookWeightFun = fun(book: Book) = book.weight
val bookPriceFun = fun(book: Book) = book.price.value
Because functions are like objects, you can even assign them to a variable. Add the main()
function below to the Functions.kt file:
fun main() {
// 1
var bookFun = bookWeightFun
println("Book weight: ${bookFun(books[0])} Kg")
// 2
bookFun = bookPriceFun
println("Book price: ${bookFun(books[0])} £")
}
Breaking down the above:
- The
bookFun
function variable is initialized using thebookWeightFun
variable. Using the first book from the collection in Books.kt,bookFun
will print out the book weight. - Reassign
bookFun
to thebookPriceFun
function and notice the price will now be printed.
Build and run. You’ll get an output like this:
Book weight: 2.1 Kg
Book price: 39.26 £
Function Types
When you define a variable, you’re implying that it can change its value. In the following code, you define the anotherBook
variable, which can reference any instance of the Book class. The type of anotherBook
is then Book. Add this code to a new file named FunctionTypes.kt:
var anotherBook = Book(
"8850330731",
"Android 3: Guida per lo sviluppatore (Italian Edition)",
642,
Price(40.06, "£"),
1.8,
2011,
"Massimo Carli"
)
You can do the same with a function. In the following code, you define the anotherBookFun
variable, which references the function you created earlier:
var anotherBookFun = fun(book: Book) = book.weight
You know that the anotherBookFun
variable can also reference any other function of the same type. So, what is the type of a function?
Again, a variable’s type is a way to abstract all the possible values the variable can reference. In this case, those are functions like anotherBookFun
and the previous bookFun
. This is like a pipe with an input of type Book and an output of type Double. With Kotlin you can represent this type using this typealias
definition. Add the following code:
typealias BookMapper<T> = (Book) -> T
This is a fundamental concept: The type of a function depends on the input and output types. In other words, what defines the type of a function is the set of possible values in input and the set of all the possible values in the output.
More importantly, the type of a function has nothing to do with how the mapping from the input and output value is done. That’s where the abstraction enters into play and where it becomes clear that FP has its own polymorphism.
To see this in action, copy this code into the FunctionTypes.kt file:
fun main() {
// 1
var mapper: BookMapper<Double> = ::bookWeight
// 2
var currency: BookMapper<String> = { book -> book.price.currency }
// 3
println("Weight of ${books[0].name} is ${mapper(books[0])} Kg")
// 4
mapper = ::bookPrice
// 5
println("Price of ${books[0].name} is ${mapper(books[0])}${currency(books[0])}")
}
Here’s what’s happening in the code above:
- Create a mapper variable, which references the function, which returns the weight of a book.
- The currency variable defines a function, which returns the currency for the given book as a string.
- Print the name of a book with its weight using the previously defined mapper variable.
- Update the mapper variable so it now refers to the function for the price of a book.
- Print the name and price of a book using the updated mapper variable.
Build and run. You’ll get an output like this:
Weight of Android 6: guida per lo sviluppatore (Italian Edition) is 2.1 Kg
Price of Android 6: guida per lo sviluppatore (Italian Edition) is 39.26£
Applying Higher-Order Functions
In the previous code, you assigned functions to variables as though they were normal objects. This is important to note because a function can be a parameter of another function or a return value. Kotlin developers are familiar with code like this, which can be added to a new HighOrderFunction.kt file:
fun List<Book>.total(fn: BookMapper<Double>): Double =
fold(0.0) { total, book -> total + fn(book) }
This creates an extension function for the List<Book> that accepts as input a function that maps a book to the value to add to the total of type Double. This is a typical example of a function that accepts another function as a parameter. To understand how it works, add the following code into the HighOrderFunction.kt file:
fun main() {
// 1
val totalPrice = books.total { it.price.value }
val totalWeight = books.total { it.weight }
// 2
println("Total Price: ${totalPrice} £")
println("Total Weight: ${totalWeight} Kg")
// 3
books.forEach { println(it.name) }
}
Here, you:
- Use a lambda expression, pass the function parameter to the total function and calculate the total price and total weight of a list of books.
- Print the totals.
- Use a predefined higher order function of Kotlin in order to print all the names of the list of books.
Higher-order functions allow us to answer the first question from the section on FP: When you program using OO, you define classes. When you use FP, you create higher-order functions.
Build and run. You’ll get an output like this:
Total Price: 212.06 £
Total Weight: 9.700000000000001 Kg
Android 6: guida per lo sviluppatore (Italian Edition)
Android 3: Guida per lo sviluppatore (Italian Edition)
Sviluppare applicazioni Android con Google Play services (Italian Edition)
Creare la prima applicazione Android - Kindle
Android 4: Guida per lo sviluppatore (Italian Edition)
RoboGuice e Robotium: Dependency Injection applicata ad Android - Kindle
Android Activity: Gestire il flusso di navigazione di un'app - Kindle
Sviluppare applicazioni per Android (Italian Edition)