Is it possible to use Kotlin's by-delegation with an existing class/object (i.e.Arrow's Either)?

555 Views Asked by At

I have a few specialized classes that I would like to create using Kotlin and Arrow and they will wrap around an Arrow Either monad. I've created the following code to use Kotlin's delegation, but I am wondering whether it can be simplified or made more idiomatic. Any suggestions would be appreciated.

Thank you for your time and interest.

internal data class EitherWrapper<L,R>(private var delegate: Either<L,R>) {
     internal operator fun getValue(thisRef: Any?, property: KProperty<*>): Either<L,R> {
        return delegate
    }

    internal operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Either<L,R>) {
        delegate = value
    }
}

fun main(args: Array<String>) {
    val delegate: Either<Exception, String> = Right("howdy")
    val myeither: Either<Exception, String> by EitherWrapper(delegate)
    println("IsLeft(): ${myeither.isLeft()}")
    println("IsRight(): ${myeither.isRight()}")
}
2

There are 2 best solutions below

0
On

So, I think that I have a solution to my query. The following code wraps an Either within a new type: Exceptional. This code is the starting point for what I want to build. Eventually, I will have something like the following:

enter image description here

I can pass an instance of this type throughout my backend service and return a more precise exception, error, or value.

interface EitherMonad<L, R> {
    val either: Either<L, R>
}

class BaseEitherMonad<L, R>(
    override val either: Either<L, R>
) : EitherMonad<L, R>

class Exceptional<R>(
    private val delegate: EitherMonad<Exception, R>
) : EitherMonad<Exception, R> by delegate {}

fun main() {

    val delegateRight = BaseEitherMonad<Exception, String>(Either.Right("howdy"))
    val exceptionalRight = Exceptional(delegateRight)
    println("IsLeft(): ${exceptionalRight.either.isLeft()}")
    println("IsRight(): ${exceptionalRight.either.isRight()}")
    exceptionalRight.either.fold({ throw it }, { println("Right: $it") })
    exceptionalRight.either.map { println(it) }

    val delegateLeft = BaseEitherMonad<Exception, String>(Either.Left(IllegalStateException("Boom!")))
    val exceptionalLeft = Exceptional(delegateLeft)
    println("IsLeft(): ${exceptionalLeft.either.isLeft()}")
    println("IsRight(): ${exceptionalLeft.either.isRight()}")
    exceptionalLeft.either.fold({ throw it }, { println("Right: $it") })
    exceptionalLeft.either.map { println(it) }
}

A Run: enter image description here

0
On

Your code is right and correct, the only improvement you could do, as far as I see, is to make it generic, and not Either-specific, as follows:

internal data class DelegateWrapper<T>(private var delegate: T) {
  internal operator fun getValue(thisRef: Any?, property: KProperty<*>): T = 
    delegate

  internal operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
    delegate = value
  }
}