//arrow-core/arrow.core/Option
common sealed class Option<out A>
If you have worked with Java at all in the past, it is very likely that you have come across a NullPointerException
at some time (other languages will throw similarly named errors in such a case). Usually this happens because some method returns null
when you weren’t expecting it and, thus, isn’t dealing with that possibility in your client code. A value of null
is often abused to represent an absent optional value. Kotlin tries to solve the problem by getting rid of null
values altogether, and providing its own special syntax Null-safety machinery based on ?
.
Arrow models the absence of values through the Option
datatype similar to how Scala, Haskell, and other FP languages handle optional values.
Option<A>
is a container for an optional value of type A
. If the value of type A
is present, the Option<A>
is an instance of Some<A>
, containing the present value of type A
. If the value is absent, the Option<A>
is the object None
.
import arrow.core.Option
import arrow.core.Some
import arrow.core.none
//sampleStart
val someValue: Option<String> = Some("I am wrapped in something")
val emptyValue: Option<String> = none()
//sampleEnd
fun main() {
println("value = $someValue")
println("emptyValue = $emptyValue")
}
Let’s write a function that may or may not give us a string, thus returning Option<String>
:
import arrow.core.None
import arrow.core.Option
import arrow.core.Some
//sampleStart
fun maybeItWillReturnSomething(flag: Boolean): Option<String> =
if (flag) Some("Found value") else None
//sampleEnd
Using getOrElse
, we can provide a default value "No value"
when the optional argument None
does not exist:
import arrow.core.None
import arrow.core.Option
import arrow.core.Some
import arrow.core.getOrElse
fun maybeItWillReturnSomething(flag: Boolean): Option<String> =
if (flag) Some("Found value") else None
val value1 =
//sampleStart
maybeItWillReturnSomething(true)
.getOrElse { "No value" }
//sampleEnd
fun main() {
println(value1)
}
import arrow.core.None
import arrow.core.Option
import arrow.core.Some
import arrow.core.getOrElse
fun maybeItWillReturnSomething(flag: Boolean): Option<String> =
if (flag) Some("Found value") else None
val value2 =
//sampleStart
maybeItWillReturnSomething(false)
.getOrElse { "No value" }
//sampleEnd
fun main() {
println(value2)
}
Checking whether option has value:
import arrow.core.None
import arrow.core.Option
import arrow.core.Some
fun maybeItWillReturnSomething(flag: Boolean): Option<String> =
if (flag) Some("Found value") else None
//sampleStart
val valueSome = maybeItWillReturnSomething(true) is None
val valueNone = maybeItWillReturnSomething(false) is None
//sampleEnd
fun main() {
println("valueSome = $valueSome")
println("valueNone = $valueNone")
}
Creating a Option<T>
of a T?
. Useful for working with values that can be nullable:
import arrow.core.Option
//sampleStart
val myString: String? = "Nullable string"
val option: Option<String> = Option.fromNullable(myString)
//sampleEnd
fun main () {
println("option = $option")
}
Option can also be used with when statements:
import arrow.core.None
import arrow.core.Option
import arrow.core.Some
//sampleStart
val someValue: Option<Double> = Some(20.0)
val value = when(someValue) {
is Some -> someValue.value
is None -> 0.0
}
//sampleEnd
fun main () {
println("value = $value")
}
import arrow.core.None
import arrow.core.Option
import arrow.core.Some
//sampleStart
val noValue: Option<Double> = None
val value = when(noValue) {
is Some -> noValue.value
is None -> 0.0
}
//sampleEnd
fun main () {
println("value = $value")
}
An alternative for pattern matching is folding. This is possible because an option could be looked at as a collection or foldable structure with either one or zero elements.
One of these operations is map
. This operation allows us to map the inner value to a different type while preserving the option:
import arrow.core.None
import arrow.core.Option
import arrow.core.Some
//sampleStart
val number: Option<Int> = Some(3)
val noNumber: Option<Int> = None
val mappedResult1 = number.map { it * 1.5 }
val mappedResult2 = noNumber.map { it * 1.5 }
//sampleEnd
fun main () {
println("number = $number")
println("noNumber = $noNumber")
println("mappedResult1 = $mappedResult1")
println("mappedResult2 = $mappedResult2")
}
Another operation is fold
. This operation will extract the value from the option, or provide a default if the value is None
import arrow.core.Option
import arrow.core.Some
val fold =
//sampleStart
Some(3).fold({ 1 }, { it * 3 })
//sampleEnd
fun main () {
println(fold)
}
import arrow.core.Option
import arrow.core.none
val fold =
//sampleStart
none<Int>().fold({ 1 }, { it * 3 })
//sampleEnd
fun main () {
println(fold)
}
Arrow also adds syntax to all datatypes so you can easily lift them into the context of Option
where needed.
import arrow.core.some
import arrow.core.none
//sampleStart
val some = 1.some()
val none = none<String>()
//sampleEnd
fun main () {
println("some = $some")
println("none = $none")
}
import arrow.core.toOption
//sampleStart
val nullString: String? = null
val valueFromNull = nullString.toOption()
val helloString: String? = "Hello"
val valueFromStr = helloString.toOption()
//sampleEnd
fun main () {
println("valueFromNull = $valueFromNull")
println("valueFromStr = $valueFromStr")
}
You can easily convert between A?
and Option<A>
by using the toOption()
extension or Option.fromNullable
constructor.
import arrow.core.firstOrNone
import arrow.core.toOption
import arrow.core.Option
//sampleStart
val foxMap = mapOf(1 to "The", 2 to "Quick", 3 to "Brown", 4 to "Fox")
val empty = foxMap.entries.firstOrNull { it.key == 5 }?.value.let { it?.toCharArray() }.toOption()
val filled = Option.fromNullable(foxMap.entries.firstOrNull { it.key == 5 }?.value.let { it?.toCharArray() })
//sampleEnd
fun main() {
println("empty = $empty")
println("filled = $filled")
}
import arrow.core.Some
fun main() {
val value =
//sampleStart
Some(1).map { it + 1 }
//sampleEnd
println(value)
}
import arrow.core.Some
val value =
//sampleStart
Some(1).zip(Some("Hello"), Some(20.0), ::Triple)
//sampleEnd
fun main() {
println(value)
}
import arrow.core.computations.option
import arrow.core.Some
import arrow.core.Option
suspend fun value(): Option<Int> =
//sampleStart
option {
val a = Some(1).bind()
val b = Some(1 + a).bind()
val c = Some(1 + b).bind()
a + b + c
}
//sampleEnd
suspend fun main() {
println(value())
}
import arrow.core.computations.option
import arrow.core.Some
import arrow.core.none
import arrow.core.Option
suspend fun value(): Option<Int> =
//sampleStart
option {
val x = none<Int>().bind()
val y = Some(1 + x).bind()
val z = Some(1 + y).bind()
x + y + z
}
//sampleEnd
suspend fun main() {
println(value())
}
Contents partially adapted from Scala Exercises Option Tutorial Originally based on the Scala Koans.
Name | Summary |
---|---|
Companion | common object Companion |
Name | Summary |
---|---|
align | common infix fun <B> align(b: Option<B>): Option<Ior<A, B» Align two options ( this on the left and b on the right) as one Option of Ior.common inline fun <B, C> align(b: Option<B>, f: (Ior<A, B>) -> C): Option<C> Align two options ( this on the left and b on the right) as one Option of Ior, and then, if it’s not None, map it using f. |
all | common inline fun all(predicate: (A) -> Boolean): Boolean Returns true if this option is empty ‘'’or’’’ the predicate $predicate returns true when applied to this $option’s value. |
and | common infix fun <X> and(value: Option<X>): Option<X> |
crosswalk | common inline fun <B> crosswalk(f: (A) -> Option<B>): Option<Option<B» |
crosswalkMap | common inline fun <K, V> crosswalkMap(f: (A) -> Map<K, V>): Map<K, Option<V» |
crosswalkNull | common inline fun <B> crosswalkNull(f: (A) -> B?): Option<B>? |
exists | common inline fun exists(predicate: (A) -> Boolean): Boolean Returns true if this option is nonempty ‘'’and’’’ the predicate $p returns true when applied to this $option’s value. Otherwise, returns false. |
filter | common inline fun filter(predicate: (A) -> Boolean): Option<A> Returns this $option if it is nonempty ‘'’and’’’ applying the predicate $p to this $option’s value returns true. Otherwise, return $none. |
filterNot | common inline fun filterNot(predicate: (A) -> Boolean): Option<A> Returns this $option if it is nonempty ‘'’and’’’ applying the predicate $p to this $option’s value returns false. Otherwise, return $none. |
findOrNull | common inline fun findOrNull(predicate: (A) -> Boolean): A? Returns the $option’s value if this option is nonempty ‘'’and’’’ the predicate $p returns true when applied to this $option’s value. Otherwise, returns null. |
flatMap | common inline fun <B> flatMap(f: (A) -> Option<B>): Option<B> Returns the result of applying $f to this $option’s value if this $option is nonempty. Returns $none if this $option is empty. Slightly different from map in that $f is expected to return an $option (which could be $none). |
fold | common inline fun <R> fold(ifEmpty: () -> R, ifSome: (A) -> R): R |
foldLeft | common inline fun <B> foldLeft(initial: B, operation: (B, A) -> B): B |
foldMap | common inline fun <B> foldMap(MB: Monoid<B>, f: (A) -> B): B |
isDefined | common fun isDefined(): Boolean Returns true if the option is an instance of Some, false otherwise. |
isEmpty | common abstract fun isEmpty(): Boolean Returns true if the option is None, false otherwise. |
isNotEmpty | common fun isNotEmpty(): Boolean |
map | common inline fun <B> map(f: (A) -> B): Option<B> Returns a Some<$B> containing the result of applying $f to this $option’s value if this $option is nonempty. Otherwise return $none. |
mapNotNull | common inline fun <B> mapNotNull(f: (A) -> B?): Option<B> Returns $none if the result of applying $f to this $option’s value is null. Otherwise returns the result. |
nonEmpty | common fun nonEmpty(): Boolean alias for isDefined |
orNull | common fun orNull(): A? |
padZip | common fun <B> padZip(other: Option<B>): Option<Pair<A?, B?» inline fun <B, C> padZip(other: Option<B>, f: (A?, B?) -> C): Option<C> |
pairLeft | common fun <L> pairLeft(left: L): Option<Pair<L, A» |
pairRight | common fun <R> pairRight(right: R): Option<Pair<A, R» |
reduceOrNull | common inline fun <B> reduceOrNull(initial: (A) -> B, operation: (B, A) -> B): B? |
reduceRightEvalOrNull | common inline fun <B> reduceRightEvalOrNull(initial: (A) -> B, operation: (A, acc: Eval<B>) -> Eval<B>): Eval<B?> |
replicate | common fun replicate(n: Int): Option<List<A» |
tap | common inline fun tap(f: (A) -> Unit): Option<A> The given function is applied as a fire and forget effect if this is a some . When applied the result is ignored and the original Some value is returned |
tapNone | common inline fun tapNone(f: () -> Unit): Option<A> The given function is applied as a fire and forget effect if this is a None . When applied the result is ignored and the original None value is returned |
toEither | common inline fun <L> toEither(ifEmpty: () -> L): Either<L, A> |
toList | common fun toList(): List<A> |
toString | common open override fun toString(): String |
traverse | common inline fun <B> traverse(fa: (A) -> Iterable<B>): List<Option<B» |
traverseEither | common inline fun <AA, B> traverseEither(fa: (A) -> Either<AA, B>): Either<AA, Option<B» |
traverseValidated | common inline fun <AA, B> traverseValidated(fa: (A) -> Validated<AA, B>): Validated<AA, Option<B» |
void | common fun void(): Option<Unit> |
zip | common fun <B> zip(other: Option<B>): Option<Pair<A, B» inline fun <B, C> zip(b: Option<B>, map: (A, B) -> C): Option<C> inline fun <B, C, D> zip(b: Option<B>, c: Option<C>, map: (A, B, C) -> D): Option<D> inline fun <B, C, D, E> zip(b: Option<B>, c: Option<C>, d: Option<D>, map: (A, B, C, D) -> E): Option<E> inline fun <B, C, D, E, F> zip(b: Option<B>, c: Option<C>, d: Option<D>, e: Option<E>, map: (A, B, C, D, E) -> F): Option<F> inline fun <B, C, D, E, F, G> zip(b: Option<B>, c: Option<C>, d: Option<D>, e: Option<E>, f: Option<F>, map: (A, B, C, D, E, F) -> G): Option<G> inline fun <B, C, D, E, F, G, H> zip(b: Option<B>, c: Option<C>, d: Option<D>, e: Option<E>, f: Option<F>, g: Option<G>, map: (A, B, C, D, E, F, G) -> H): Option<H> inline fun <B, C, D, E, F, G, H, I> zip(b: Option<B>, c: Option<C>, d: Option<D>, e: Option<E>, f: Option<F>, g: Option<G>, h: Option<H>, map: (A, B, C, D, E, F, G, H) -> I): Option<I> inline fun <B, C, D, E, F, G, H, I, J> zip(b: Option<B>, c: Option<C>, d: Option<D>, e: Option<E>, f: Option<F>, g: Option<G>, h: Option<H>, i: Option<I>, map: (A, B, C, D, E, F, G, H, I) -> J): Option<J> inline fun <B, C, D, E, F, G, H, I, J, K> zip(b: Option<B>, c: Option<C>, d: Option<D>, e: Option<E>, f: Option<F>, g: Option<G>, h: Option<H>, i: Option<I>, j: Option<J>, map: (A, B, C, D, E, F, G, H, I, J) -> K): Option<K> |
Name |
---|
None |
Some |
Name | Summary |
---|---|
combine | common fun <A> Option<A>.combine(SGA: Semigroup<A>, b: Option<A>): Option<A> |
combineAll | common |
compareTo | common operator fun <A : Comparable<A» Option<A>.compareTo(other: Option<A>): Int |
ensure | common inline fun <A> Option<A>.ensure(error: () -> Unit, predicate: (A) -> Boolean): Option<A> |
filterIsInstance | common inline fun <B> Option<*>.filterIsInstance(): Option<B> Returns an Option containing all elements that are instances of specified type parameter B. |
flatten | common fun <A> Option<Option<A».flatten(): Option<A> |
getOrElse | common inline fun <T> Option<T>.getOrElse(default: () -> T): T Returns the option’s value if the option is nonempty, otherwise return the result of evaluating default . |
handleError | common inline fun <A> Option<A>.handleError(f: (Unit) -> A): Option<A> |
handleErrorWith | common inline fun <A> Option<A>.handleErrorWith(f: (Unit) -> Option<A>): Option<A> |
or | common infix fun <T> Option<T>.or(value: Option<T>): Option<T> |
orElse | common inline fun <A> Option<A>.orElse(alternative: () -> Option<A>): Option<A> Returns this option’s if the option is nonempty, otherwise returns another option provided lazily by default . |
redeem | common inline fun <A, B> Option<A>.redeem(fe: (Unit) -> B, fb: (A) -> B): Option<B> |
redeemWith | common inline fun <A, B> Option<A>.redeemWith(fe: (Unit) -> Option<B>, fb: (A) -> Option<B>): Option<B> |
replicate | common fun <A> Option<A>.replicate(n: Int, MA: Monoid<A>): Option<A> |
rethrow | common fun <A> Option<Either<Unit, A».rethrow(): Option<A> |
salign | common fun <A> Option<A>.salign(SA: Semigroup<A>, b: Option<A>): Option<A> |
separateEither | common fun <A, B> Option<Either<A, B».separateEither(): Pair<Option<A>, Option<B» Separate the inner Either value into the Either.Left and Either.Right. |
separateValidated | common fun <A, B> Option<Validated<A, B».separateValidated(): Pair<Option<A>, Option<B» Separate the inner Validated value into the Validated.Invalid and Validated.Valid. |
sequence | common fun <A> Option<Iterable<A».sequence(): List<Option<A» |
sequenceEither | common fun <A, B> Option<Either<A, B».sequenceEither(): Either<A, Option<B» |
sequenceValidated | common fun <A, B> Option<Validated<A, B».sequenceValidated(): Validated<A, Option<B» |
toMap | common fun <K, V> Option<Pair<K, V».toMap(): Map<K, V> |
unalign | common fun <A, B> Option<Ior<A, B».unalign(): Pair<Option<A>, Option<B» inline fun <A, B, C> Option<C>.unalign(f: (C) -> Ior<A, B>): Pair<Option<A>, Option<B» |
unite | common fun <A> Option<Iterable<A».unite(MA: Monoid<A>): Option<A> |
uniteEither | common fun <A, B> Option<Either<A, B».uniteEither(): Option<B> |
uniteValidated | common fun <A, B> Option<Validated<A, B».uniteValidated(): Option<B> |
unzip | common fun <A, B> Option<Pair<A, B».unzip(): Pair<Option<A>, Option<B» inline fun <A, B, C> Option<C>.unzip(f: (C) -> Pair<A, B>): Pair<Option<A>, Option<B» |
widen | common fun <B, A : B> Option<A>.widen(): Option<B> Given A is a sub type of B, re-type this value from Option to Option |
Do you like Arrow?
✖