Functional Programming with Kotlin and Arrow: Getting Started
- Getting Started
- OOP in a Nutshell
- Types as Abstractions
- Abstracting Identity
- 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?
Fundamentals are, of course, fundamental — and fun! This is particularly true for functional programming (FP). You may already use FP in your Kotlin code. If you’re curious about what’s behind this important paradigm, this is the tutorial for you.
This functional programming tutorial dives deeper into the following topics:
- Function types
- Side effects
- Higher-order functions
- Abstraction and composition
- The Arrow framework
Writing some code examples, you’ll learn how to “think functionally” and how FP can make your code more testable and safe. In order to go fast, we need to go well so let’s start!
First, click the Download Materials button at the top or the bottom of the page. You’ll see the initial and final code, but you’ll write most of it during this tutorial. Open the starter project in IntelliJ IDEA. In the Books.kt file, you’ll find the definition of the
data class Price(val value: Double, val currency: String = "$") data class Book( val ISDN: String, val name: String, val pages: Int, val price: Price, val weight: Double, val year: Int, val author: String )
You’ll also find a list of instances used to initialize the
Just as Object Oriented Programming (OOP) means working with objects, Functional Programming (FP) means working with functions. This is the start of your journey from OOP to FP. Fasten your seatbelt!
OOP in a Nutshell
The main concept in OOP is the class because it’s the first construct that you can use to describe the makeup of objects — instances of that class — in terms of properties and operations.
To start, open Books.kt. Now, to create an instance of the
Book class, invoke one of its constructors and assign the returned reference to an
androidBook variable in
val androidBook = Book( "8850333404", "Android 6: guida per lo sviluppatore (Italian Edition)", 846, Price(39.26, "£"), 2.1, 2016, "Massimo Carli" )
Book is not just a class but also defines a type.
Book is a type of
androidBook variable that can reference any other object of the same type. While an object is an instance of a specific class, its reference can be assigned to a variable of different types. Add this code at the end of
val obj: Any = androidBook println("obj description: $obj")
This assignment is possible because a
Book is an extension of
Any. Stated another way,
Any is an abstraction of
Build and run. You should see the description for
obj in the Run window.
obj name: Book(ISDN=8850333404, name=Android 6: guida per lo sviluppatore (Italian Edition), pages=846, price=Price(value=39.26, currency=£), weight=2.1, year=2016, author=Massimo Carli)
Types as Abstractions
Abstraction is the most important concept in software development. An alternative name for abstraction is subtraction. In this context, abstracting means including only what’s necessary.
androidBook in the previous code is of the type
Book because you’re interested in its properties such as
price. When you use the
obj variable of type
Any, it means that you don’t care about the book properties. You just care about the fact that it is an object.
Abstraction makes one object equal to another. A lion isn’t a tiger, but they’re equal if you think of them as carnivorous animals. In the same way, think of a type of a variable as a way of representing all the possible values that the same variable can reference.
Add the following code to
val myBook: Book = androidBook println("myBook description: $myBook")
myBook is a
Book which means it can contain, or reference, any element in the set of all possible books. A type, then, is a way to represent a set of values. A class describes the makeup of all its instances, and the type abstracts the set of all of them.
Build and run. The descriptions for
myBook match as expected.
obj description: Book(ISDN=8850333404, name=Android 6: guida per lo sviluppatore (Italian Edition), pages=846, price=Price(value=39.26, currency=£), weight=2.1, year=2016, author=Massimo Carli) myBook description: Book(ISDN=8850333404, name=Android 6: guida per lo sviluppatore (Italian Edition), pages=846, price=Price(value=39.26, currency=£), weight=2.1, year=2016, author=Massimo Carli)
In creating a program, you define how objects interact with each other. Generally, objects are instances of classes you design for them to collaborate.
One of the main benefits of OOP is encapsulation, in which objects interact using their interfaces, or a set of operations that other objects see and can invoke. Unfortunately, objects are not very good in a multithreaded environment because they encapsulate what really matters — the mutable state.
Objects hide the way they mutate their states and, because they collaborate, they share data. Mutable and shared states are the primary cause of data races, which often result in bugs.
A data race happens when you have multiple threads accessing the same mutable state in an unsafe way. Even without FP, it’s possible to solve this problem by removing one of the causes.
A data race can’t exist if you have only one thread. Many systems use this Single Thread Model for UI management, allowing a single main thread — in this case, the UI thread — to access UI components.
Another option is not to allow mutable states. This is the case when you create immutable objects, which are thread-safe because they can’t change their state after creation.
Using immutable objects is a good practice in both OOP and FP. Your
Book class is already immutable because of val instead of var in the definition of its properties.
To provide a mutable version of the same class, make it explicit defining the MutableBook class. Add the following code to Books.kt:
data class MutableBook( var ISDN: String, var name: String, var pages: Int, var price: Price, var weight: Double, var year: Int, var author: String )
Moving From Objects to Functions
You might have some key questions about moving from objects to functions:
- What’s the equivalent of a class in an FP world?
- What’s the equivalent of an instance?
- Can you use the same logical path you used with classes and objects?
- What about types, abstraction and collaboration? Can a function be the solution to the data race problem you face in OOP?
Read on for the answers to these questions!