Kotlin 实现Rust风格Result

2021-10-16  本文已影响0人  Gascognya
sealed interface KResult<T, E>{
    fun bool(): Boolean = this is Ok

    fun option(): T? = when(this){
        is Err -> null
        is Ok -> value
    }

    fun <U> map(op: (T) -> U): KResult<U, E> = when(this){
        is Err -> err(this)
        is Ok -> ok(op(value))
    }

    infix fun and(other: KResult<T, E>): KResult<T, E> = when(this){
        is Err -> other
        is Ok -> this
    }

    infix fun or(other: KResult<T, E>): KResult<T, E> = when(this){
        is Err -> other
        is Ok -> this
    }

    fun <U> andThen(op: (T) -> KResult<U, E>): KResult<U, E> = when(this){
        is Err -> err(this)
        is Ok -> op(value)
    }
    
    fun <U> orElse(op: (E) -> KResult<T, U>): KResult<T, U> = when(this){
        is Err -> op(error)
        is Ok -> ok(this)
    }

}

data class Ok<T, E> internal constructor(val value: T): KResult<T, E>
data class Err<T, E> internal constructor(val error: E): KResult<T, E>

fun <T, E> ok(value: T): KResult<T, E> = Ok(value)
fun <T, E> ok(old: Ok<T, *>): KResult<T, E> = old as KResult<T, E>
fun <E> ok(): KResult<Any?, E> = Ok(null)

fun <T, E> err(error: E): KResult<T, E> = Err(error)
fun <T, E> err(old: Err<*, E>): KResult<T, E> = old as KResult<T, E>
fun <T> err(): KResult<T, Any?> = Err(null)

fun<T> T?.asRes(): KResult<T, Any?> = if (this == null) err() else ok(this)
fun<T, E> T?.asRes(error: E): KResult<T, E> = if (this == null) err(error) else ok(this)

fun<T> Optional<T>.res(): KResult<T, Any?> = if (isEmpty) err() else ok(get())
fun<T, E> Optional<T>.res(error: E): KResult<T, E> = if (isEmpty) err(error) else ok(get())

fun<T> Result<T>.res(): KResult<T, Any?> = if (isFailure) err() else ok(getOrThrow())
fun<T, E> Result<T>.res(error: E): KResult<T, E> = if (isFailure) err(error) else ok(getOrThrow())

因为kotlin也可以像rust那样,通过返回值类型反向推导泛型。这样有实际意义的Result成为可能。

起名IResult是因为kotlin自带一个简单的Result。会产生命名冲突。故采用C#接口命名法作为新名称

Empty是个简单的Any?别名,因为在只有半边有值的情况下,为了能写null值。IResult<Any?, T> 和IResult<T, Any?>可以改写为IResult<Empty, T>, IResult<T, Empty>
当然你也可以写个新的IResult<TOk>, IResult<TErr>,但是因为Kotlin不能像C#那样识别出来不同,所以同名不同泛型在Kotlin中是不允许的。你可以改个名,当然前提是你觉得足够优雅。

上述代码只是今天随便弄的简版,你可以所以加糖

上一篇 下一篇

猜你喜欢

热点阅读