A pure function is a function that consistently returns the same output when given the same input. Pure functions exhibit a deterministic behavior and cause no observable effects externally. We call this property referential transparency.
Referential transparency allows us to reason about the different pieces of our program in isolation.
To create a pure function in Kotlin, let’s use the keyword fun
:
//sampleStart
fun helloWorld(): String =
"Hello World"
//sampleEnd
fun main() {
println(helloWorld())
}
We can state that helloWorld
is a pure and referentially transparent function because invoking helloWorld()
consistently returns the same output given the same input, and does not produce observable changes in the external world.
A side effect is an externally observable effect a function performs in addition to returning a value.
Performing network or file I/O, writing to streams, and, in general, all functions that return Unit
are very likely to produce side effects. That’s because a Unit
return value denotes no useful return, which implies that the function does nothing but perform effects.
In Arrow Fx, we use suspend fun
to denote a function that may cause side effects when invoked.
In the example below, println(a : Any): Unit
is a side effect because, every time it’s invoked, it causes observable effects by interacting with the System.out
stream and signals that it produces no useful output by returning Unit
.
When we denote side effects as suspend
, the Kotlin compiler will ensure that we’re not applying uncontrolled side effects in the pure environment.
//sampleStart
fun helloWorld(): String =
"Hello World"
suspend fun sayHello(): Unit =
println(helloWorld())
fun main() {
sayHello()
}
//sampleEnd
Compiling the snippet above will result in javax.script.ScriptException: error: suspend function 'sayHello' should be called only from a coroutine or another suspend function
compilation error for sayHello
call.
The Kotlin compiler disallows the invocation of suspended functions in the pure environment because suspend fun
requires declaration inside another suspended function or a continuation.
A continuation is a Kotlin Interface, Continuation<A>
, that proves we know how to handle success and error cases resulting from running the suspended effect.
This is a great built-in feature of the Kotlin compiler that already makes it an ideal choice for Typed FP, but it’s not the only one.
Continue reading on if you’re curious to see how the Kotlin Compiler and Arrow Fx can eliminate many of the functional idioms by using direct syntax, over effect monads.
suspend
compositionApplying and composing suspended side effects is allowed in the presence of other suspended side effects.
In the example below, sayHello
and sayGoodBye
are valid inside greet
because they are all suspended functions.
suspend fun sayGoodBye(): Unit =
println("Good bye World!")
suspend fun sayHello(): Unit =
println("Hello World")
suspend fun greet(): Unit {
sayHello() // this is ok because
sayGoodBye() // `greet` is also `suspend`
}
The greet
program is ready to run as soon as the user is ready to commit to an execution strategy that is either blocking
or non-blocking
.
blocking
execution strategies will block the current thread that’s waiting for the program to yield a value, whereas non-blocking
strategies will immediately return and perform the program’s work without blocking the current thread.
Since both blocking and non-blocking execution scenarios perform side effects, we consider running effects as an unsafe
operation.
KotlinX Coroutines offers runBlocking
, launch
and async
to run a suspended program.
Usage of unsafe runner functions (like runBlocking
in this case) is reserved for the end of the world and may be the only impure execution of a well-typed functional program.
launch
and async
can however also be used in pure/safe ways if they’re used within another suspend fun
.
import kotlinx.coroutines.*
import arrow.fx.coroutines.*
//sampleStart
suspend fun sayHello(): Unit =
println("Hello World")
suspend fun sayGoodBye(): Unit =
println("Good bye World!")
suspend fun greet(): Unit {
sayHello()
sayGoodBye()
}
fun main(): Unit = runBlocking { // The edge of our world
greet()
}
//sampleEnd
Arrow Fx emphasizes the guarantee that users understand where they are performing side effects in their program declaration.
If you’re not very familiar with Functional Programming, and you’ve made it this far, you may realize that, despite the buzzwords and some FP jargon, you already know how to use Arrow Fx in general. This is because Arrow Fx brings the most popular imperative style to effectful programs with few simple primitives for effect control and asynchronous programming.
Arrow Fx offers an idiomatic way of doing effectfull FP with Kotlin’s coroutine system. It does this by providing support for cancellation, error handling, resource handling, and all features you might be familiar with from other functional effect systems.
Arrow Fx offers direct style syntax and effect control without compromises, and removes the syntactic burden of type parametrization while still yielding programs that are pure, safe, and referentially transparent.
Come check out Arrow fx and join the discussion in the Kotlin Slack and Gitter
Do you like Arrow?
✖