Why use Arrow's Options instead of Kotlin nullable

Related searches

I was having a look at the Arrow library found here. Why would ever want to use an Option type instead of Kotlin's built in nullables?

Disclaimer: If you really want to have a detailed talk about why Arrow is useful, then please head over to https://soundcloud.com/user-38099918/arrow-functional-library and listen to one of the people who work on it. (5:35min)

The people who create and use that library simple want to use Kotlin differently than the people who created it and use "the Option datatype similar to how Scala, Haskell and other FP languages handle optional values".

This is just another way of defining return types of values that you do not know the output of.

Let me show you three versions:

nullability in Kotlin

val someString: String? = if (condition) "String" else null

object with another value

val someString: String = if (condition) "String" else ""

the Arrow version

val someString: Option<String> = if (condition) Some("String") else None

A major part of Kotlin logic can be to never use nullable types like String?, but you will need to use it when interopting with Java. When doing that you need to use safe calls like string?.split("a") or the not-null assertion string!!.split("a").

I think it is perfectly valid to use safe calls when using Java libraries, but the Arrow guys seem to think different and want to use their logic all the time.

The benefit of using the Arrow logic is "empowering users to define pure FP apps and libraries built atop higher order abstractions. Use the below list to learn more about Λrrow's main features".

Advantages and Pitfalls of Arrow Functions | by Tim Fogarty, Whereas arrow functions use the value of this in their lexical scope. This leads to very different behaviour. What's the difference between� Why Use Arrow’s Financing and Leasing Services When Was the Last Time You Did Business With Arrow Capital Solutions? The majority of U.S. businesses are financing the equipment they need to conduct critical business operations.

I have been using Option data type provided by Arrow for over a year and there at the begining we did the exact same question to ourselves. The answer follows.

Option vs Nullable

If you compare just the option data type with nullables in Kotlin they are almost even. Same semanthics (there is some value or not), almost same syntax (with Option you use map, with nullables you use safe call operator).

But when using Options you enable the possibility to take benefits from the arrow ecosystem!

Arrow ecosystem (functional ecosystem)

When using Options you are using the Monad Pattern. When using the monad pattern with liberaries like arrow, scala cats, scalaz, you can take benefits from several functional concepts. Just 3 examples of benefits (there is a lot more than that):

1. Access to other Monads

Option is not the only one! For instance, Either is a lot useful to express and avoid to throw Exceptions. Try, Validated and IO are examples of other common monads that help us to do (in a better way) things we do on typical projects.

2. Conversion between monads + abstractions

You can easily convert one monad to another. You have a Try but want to return (and express) an Either? Just convert to it. You have an Either but doesn't care about the error? Just convert to Option.

val foo = Try { 2 / 0 }
val bar = foo.toEither()
val baz = bar.toOption()

This abstraction also helps you to create functions that doens't care about the container (monad) itself, just about the content. For example, you can create a extension method Sum(anyContainerWithBigDecimalInside, anotherContainerWithBigDecimal) that works with ANY MONAD (to be more precise: "to any instance of applicative") this way:

fun <F> Applicative<F>.sum(vararg kinds: Kind<F, BigDecimal>): Kind<F, BigDecimal> {
    return kinds.reduce { kindA, kindB ->
        map(kindA, kindB) { (a, b) -> a.add(b) }
    }
}

A little complex to understand, but very helpful and easy to use.

3. Monad comprehensions

Going from nullables to monads is not just about changing safe call operators to map calls. Take a look at the "binding" feature that arrow provides as the implementation of the pattern "Monad Comprehensions":

fun calculateRocketBoost(rocketStatus: RocketStatus): Option<Double> {
    return binding {
        val (gravity) = rocketStatus.gravity
        val (currentSpeed) = rocketStatus.currentSpeed
        val (fuel) = rocketStatus.fuel
        val (science) = calculateRocketScienceStuff(rocketStatus)
        val fuelConsumptionRate = Math.pow(gravity, fuel)
        val universeStuff = Math.log(fuelConsumptionRate * science)

        universeStuff * currentSpeed
    }
}

All the functions used and also the properties from rocketStatus parameter in the above example are Options. Inside the binding block the flatMap call is abstracted for us. The code is a lot easier to read (and write) and you don't need to check if the values are present, if some of them is not, the computation will stop and the result will be an Option with None!

Now try to imagine this code with null verifications instead. Not just safe call operators but also probably if null then return code paths. A lot harder isn't it?

Also, the above example uses Option but the true power about monad comprehensions as an abastraction is when you use it with monads like IO in which you can abstract asynchronous code execution in the exact same "clean, sequential and imperative" way as above :O

Conclusion

I strongly recommend you to start using monads like Option, Either, etc as soon as you see the concept fits the semanthics you need, even if you are not sure if you will take the other big benefits from the functional ecosystem or if you doesn't know them very well yet. Soon you'll be using it without noticing the learning-curve. That In my company we use it in almost all Kotlin projects, even in the object-oriented ones (which are the majority).

When (and why) you should use ES6 arrow functions — and when , by Cynthia Lee. When (and why) you should use ES6 arrow functions — and when you shouldn't. Arrow functions (also called “fat arrow� Our customers typically benefit from: Improved efficiency, flexibility, scalability and reliability Reduced risk and total costs Improved ability to focus on critical priorities while Arrow does the legwork

One thing other answers haven't mentioned: you can have Option<Option<SomeType>> where you can't have SomeType??. Or Option<SomeType?>, for that matter. This is quite useful for compositionality. E.g. consider Kotlin's Map.get:

abstract operator fun get(key: K): V?

Returns the value corresponding to the given key, or null if such a key is not present in the map.

But what if V is a nullable type? Then when get returns null it can be because the map stored a null value for the given key or because there was no value; you can't tell! If it returned Option<V>, there wouldn't be a problem.

When should I use arrow functions with React?, But there is a much better reason to use arrow functions… #Arrows prevent this bugs. Arrow functions don't redefine the value of this within their� Another common and extremely powerful use for arrow functions is to encapsulate object transformations. For example, in Vue.js there is a common pattern for including pieces of a Vuex store directly into a Vue component using mapState.

When should I use Arrow functions in ECMAScript 6?, A while ago our team migrated all its code (a mid-sized AngularJS app) to JavaScript compiled using Traceur Babel. I'm now using the following rule of thumb for� When you should use them. Arrow functions shine best with anything that requires this to be bound to the context, and not the function itself. Despite the fact that they are anonymous, I also like using them with methods such as map and reduce, because I think it makes my code more readable.

When 'Not' to Use Arrow Functions, 1a. Object literal. Since arrow function has a short syntax, it's inviting to use it for a method definition. Let's take a try: const calculate = { array: [1,� An arrow function expression is a syntactically compact alternative to a regular function expression, although without its own bindings to the this, arguments, super, or new.target keywords. Arrow function expressions are all suited as methods, and they cannot be used as constructors. JavaScript Demo: Functions =>

You’re working in a worksheet in Excel and you press one of the arrow keys on your keyboard to move to the next cell. But instead of moving to the next cell, the whole worksheet moved. Don’t panic. There’s an easy fix for this.

Comments
  • already read the docs? arrow-kt.io/docs/datatypes/option
  • @s1m0nw1 That states "Kotlin tries to solve the problem by getting rid of null values altogether and providing its own special syntax Null-safety machinery based on ?." and "Arrow models the absence of values through the Option datatype similar to how Scala, Haskell and other FP languages handle optional values." It does not provide any comparison between the two approaches.
  • To the question "why should I use Option instead of nullables" I would expect concrete reasons/advantages, especially given the additional syntax overhead and non-conformity with standard types. However this answer merely states "so you can use Kotlin in a different way".
  • A few nullable extension functions and the rocket status stuff can be reduced to rocketStatus.gravity?.pow(rocketStatus.fuel)?.times(calculateRocketScienceStuff(rocketStatus))?.log2()?.times(rocketStatus.currentSpeed) which in my opinion is much simpler and it ihas zero cost.
  • @Fleshgrinder it may be personal preference but for me this code is a lot harder to understand and to debug than the imperative version showed in my example. Also if you need to write extension functions (and obviously to mantain it) it has a cost.
  • And that was an isolated example. With monad comprehensions we can do other things, like for example to abstract in the same simple and imparative way asynchronous operations
  • But you can use Optional<T?> in this case and you have containsKey.
  • 1. Yes, it's explicitly mentioned in the answer. But then you still need Option, which is what the question is about. 2. Using containsKey is worse in at least two ways: accessing the map twice isn't free; in a concurrent program the key can be removed by another thread between containsKey and get calls.
  • I didn't consider Option and Optional to be the same. Concurrency is a different issue, you have synchronized for that. In any event, it's an extremely narrow use case that I've only ever encountered in caches.
  • If you mean the Java Optional, then of course Optional<T?> won't work; it doesn't allow nulls.
  • val value: String? = mapOf<String, Optional<String>>()["k"]?.orElse(null) goal achieved as we can now distinguish between missing key (get returns null) and a null value (optional is empty). This is how pretty much every cache implementation works that allows null values to be stored.