rednifre
08/06/2022, 1:03 PMbind
extension function on all nullable types, that is only available inside of a nullable
block.
I tried to do it like this, but the bind
function is not reachable. What’s the proper way to do this?
class BindNullableException : IllegalAccessException()
fun interface NullableBlock<A> : () -> A {
fun <A> A?.bind() = this ?: throw BindNullableException()
}
fun <A> nullable(block: NullableBlock<A>): A? =
try {
block()
} catch (e: BindNullableException) {
null
}
fun main() {
val a = getNullableA().bind() // should not work
val nullableC = nullable {
val a = getNullableA().bind() // should work
val b = getNullableB().bind()
getNullableC(a, b)
}
}
fun getNullableA(): String? = null
fun getNullableB(): String? = null
fun getNullableC(a: String, b: String): String? = null
tseisel
08/06/2022, 3:01 PMOption<T>
type that does exactly what you want, maybe you can look at its source code to understand how it works internally: https://arrow-kt.io/docs/apidocs/arrow-core/arrow.core/-option/rednifre
08/06/2022, 4:02 PMnullable
being a function that takes a block, in their implementation it’s an invokable object though. I’m also not sure why their monad comprehensions are suspend functions, that seems unnecessary?class BindNullableException : IllegalAccessException()
class NullableBlock<A>(val func: NullableBlock<A>.() -> A?) : () -> A {
fun <B> B?.bind(): B = this ?: throw BindNullableException()
override fun invoke(): A = func() ?: throw BindNullableException()
}
@Suppress("ClassName")
object nullable {
operator fun <A> invoke(func: NullableBlock<A>.() -> A?): A? = try {
NullableBlock(func)()
} catch (e: BindNullableException) {
null
}
}
fun main() {
val cantWork = getNullableA().bind() // should not work
val nullableC = nullable {
val a = getNullableA().bind() // should work
val b = getNullableB(a).bind()
getNullableC(a, b)
}
println("Got $nullableC")
}
fun getNullableA(): String? = null
fun getNullableB(a: String): String? = null
fun getNullableC(a: String, b: String): String? = null