https://kotlinlang.org logo
#arrow
Title
ł

Łukasz Gendek

02/06/2023, 12:47 PM
Hello, given the following code
Copy code
import arrow.core.Either
import arrow.core.continuations.Raise
import arrow.core.continuations.either

object DivisionByZero
data class IntegerParseError(val value: String)

context(Raise<IntegerParseError>)
fun parseIntegerCt(a: String) : Int = a.toIntOrNull() ?: raise(IntegerParseError(a))

context(Raise<DivisionByZero>)
fun divideIntegersCt(a: Int, b: Int): Int = if (b == 0)
   raise(DivisionByZero)
else
   a / b


context(Raise<IntegerParseError>, Raise<DivisionByZero>)
fun divideStrings(a: String, b: String): Int = divideIntegersCt(parseIntegerCt(a), parseIntegerCt(b))

fun parseInteger(a: String) : Either<IntegerParseError, Int> = either { parseIntegerCt(a) }

fun divideIntegers(a: Int, b: Int): Either<DivisionByZero, Int> = either {divideIntegersCt(a, b)}

fun divideStrings(a: String, b: String) : Either<IntegerParseError, Either<DivisionByZero, Int>> {
   TODO()
   //how to call divideStringsCt and return Either containing all possible cases
   //could it be implemented it in a generic way?
}
I am not sure how to implement the divideStrings method, Could you help me?
s

simon.vergauwen

02/06/2023, 12:54 PM
Hey @Łukasz Gendek, You can simply call:
Copy code
context(Raise<IntegerParseError>, Raise<DivisionByZero>)
fun divideStrings(a: String, b: String): Int =
While providing the order in which you want the errors to be returned, int his case it should be inferable from the return type.
Copy code
fun divideStrings(a: String, b: String) : Either<IntegerParseError, Either<DivisionByZero, Int>> = either {
  either { divideStrings(a, b) }
}
Are these signatures not conflicting though? 🤔
Copy code
context(Raise<IntegerParseError>, Raise<DivisionByZero>)
fun divideStrings(a: String, b: String): Int

fun divideStrings(a: String, b: String) : Either<IntegerParseError, Either<DivisionByZero, Int>>
Also not sure what you meant with:
Copy code
// could it be implemented it in a generic way?
If you were trying to build an example using the other
Either
functions you can do it the same as the other one but you'd be required calling
bind
.
Copy code
fun divideStrings(a: String, b: String) : Either<IntegerParseError, Either<DivisionByZero, Int>> = either {
  either {
    divideIntegers(
      parseInteger(a).bind(),
      parseInteger(b).bind()
    ).bind()
  }
}
ł

Łukasz Gendek

02/06/2023, 3:59 PM
Thank you for your quick response. It is extremely useful for me.
Copy code
fun divideStrings(a: String, b: String) : Either<IntegerParseError, Either<DivisionByZero, Int>> = either {
  either { divideStrings(a, b) }
}
By the generic implementation I meant the code below, but it unfortunately does not compile.
But it is the context receivers not the arrow to blame.
s

simon.vergauwen

02/06/2023, 4:14 PM
Ah, that is currently a bug in Kotlin compiler afaik
y

Youssef Shoaib [MOD]

02/07/2023, 7:48 PM
You can suppress that error BTW. I believe it is
@Suppress("SUBTYPING_BETWEEN_CONTEXT_RECEIVERS")
ł

Łukasz Gendek

02/08/2023, 9:58 AM
thank you, it helped with the subtyping error, but still cannot call the block function
image.png
s

simon.vergauwen

02/08/2023, 9:59 AM
I think you have to manually wire the context here 😞 Does this work?
Copy code
either outer@ {
  either inner@ {
    block(outer, inner)
  }
}
It can be that you also have to add this workaround: Adding
TypePlacedHolder<C>
into the argument position of the lambda, or the call sites won't resolve correctly.
Copy code
@OptIn(ExperimentalContracts::class)
 inline fun <A, B, C, R> with(a: A, b: B, c: C, block: context(A, B, C) (TypePlacedHolder<C>) -> R): R {
     contract { callsInPlace(block, EXACTLY_ONCE) }
     return block(a, b, c, TypePlacedHolder)
 }

 sealed interface TypePlacedHolder<out A> {
     companion object : TypePlacedHolder<Nothing>
 }
ł

Łukasz Gendek

02/08/2023, 10:04 AM
Copy code
@Suppress("SUBTYPING_BETWEEN_CONTEXT_RECEIVERS")
public fun <E1, E2, A> eitherC(block: context(Raise<E1>, Raise<E2>)() -> A): Either<E1, Either<E2, A>> = either outer@{
   either inner@{
      block(this@outer, this@inner)
   }
}
it works, thank you
I've just updated the arrow library and I can see these shiny new colours (the 'raise' method). Very helpful and very nice.
🙂
16 Views