arrow-kt / arrow

Λrrow - Functional companion to Kotlin's Standard Library
http://arrow-kt.io
Other
6.17k stars 451 forks source link

["Request"] Declarative (sub-)type checks yielding Option<*> #3493

Open postfixNotation opened 3 weeks ago

postfixNotation commented 3 weeks ago

Arrow provides this handy public fun <T> T?.toOption(): Option<T> = this?.let { Some(it) } ?: None extension function which converts nullable types to Option dtos.

There are some use cases I have where having this functionality for subtype checks is pretty handy replacing imperative is-checks.

sealed interface A
interface ASub : A

val aMaybe: A? = null

// This is what we have in Arrow already
val aOption: Option<A> = aMaybe.toOption()

// Option 1
val aSubOption: Option<ASub> = aProvider().toOption<ASub>()

// Option 2
//val aSubOption: Option<ASub> = aProvider().toOption<ASub, A>()

// Option 1
inline fun <reified T> Any?.toOption(): Option<T> = (this as? T)?.let { Some(it) } ?: None

// Option 2
//inline fun <reified S : T, T> T?.toOption(): Option<S> = (this as? S)?.let { Some(it) } ?: None

fun aProvider(): A {
    return object : ASub {}
}

I guess it's clear what I'm trying to say. The above is just an illustration to depict my thoughts.

hoc081098 commented 3 weeks ago

How about

postfixNotation commented 3 weeks ago

Good and helpful ideas, but if you have many type checks you might still end up with lots of boilerplate. Reducing boilerplate was probably also the intention of Arrow's toOption method on nullable types.

The first proposal might throw a ClassCastException as far as I know.

serras commented 1 week ago

I'm not sure that this belongs to Arrow, to be honest. I don't think the pattern of moving to Option + making a subtype check is widely used (this is the first time I hear about it, in fact). I think that some of the options raised by @hoc081098 (like (x as? T).toOption() are much clearer than x.toOption<T>().