Jordi Pradel
12/21/2022, 12:05 AMcontext(Raise<ReadError>) fun readInt(json: Json): Int
instead of fun readInt(json: Json): Either<ReadError, Int>
. Now I want to accumulate several of these errors, like we do with Either
(readInt(jsonA).zip({readInt(jsonB)},{a, b -> a + b})
). What would be the idiomatic way? I hoped for something like zip({readInt(jsonA}, {readInt(jsonB)}, {a,b -> a + b})
but it seems I can't find it in the 2.0.0 SNAPSHOT.Jordi Pradel
12/21/2022, 12:09 AMcontext (Raise<NonEmptyList<E>>)
fun <E, A, B, Z> zip(
a: Raise<E>.() -> A,
b: Raise<E>.() -> B,
transform: (A, B) -> Z,
): Z {
val aa = either(a)
val bb = either(b)
val z = aa.zip(bb, transform)
return z.getOrRaise()
}
But either(a)
seems to shortcircuit and return from my zip function with just the first error. I can't understand why.simon.vergauwen
12/21/2022, 7:29 AMsimon.vergauwen
12/21/2022, 7:31 AMcarbaj0
12/21/2022, 7:34 AMsimon.vergauwen
12/21/2022, 7:35 AMcontext
receivers but it's actually just bind
, right?simon.vergauwen
12/21/2022, 7:36 AMgetOrRaise
if maybe a nicer name if you're always working with Raise
🤔Jordi Pradel
12/21/2022, 11:27 AMHey, such a function is not available yet, and the one you shared should be correct, it should not be short-circuiting 🤔 That would be a bug.I will try to write some simplified example to see if I isolate the bug or I find something I was doing wrong. If it is the former, I'll reach you to see if it helps.
Jordi Pradel
12/21/2022, 11:30 AMEither
) would be really interesting.simon.vergauwen
12/21/2022, 12:25 PMzip
is available for Raise
yet. Feel free to open a issue for that on Github.
Problem with traverse
like operators is that it requires context receivers to do it nicely. https://github.com/arrow-kt/arrow/pull/2872Jordi Pradel
12/22/2022, 9:55 AMI will try to write some simplified example to see if I isolate the bug or I find something I was doing wrong. If it is the former, I'll reach you to see if it helps. (edited)I managed to get a simpler test failing... only to discover it seems to fail only when using context receivers. In case anyone wonders:
context(Raise<String>)
fun <A> transformError(eff: context(Raise<String>) () -> A): A =
eagerEffect { eff() } recover { raise("$it!") }
context(Raise<NonEmptyList<E>>)
fun <E, A, B, Z> zip(a: Raise<E>.() -> A, b: Raise<E>.() -> B, transform: (A, B) -> Z): Z =
either(a).zip(either(b), transform).bind()
class NestedRaiseTest : FunSpec() {
context(Raise<String>) fun fail1(): String = transformError { raise("hello") }
context(Raise<String>) fun fail2(): String = transformError { raise("world") }
init {
test("zip should zip the effects") {
eagerEffect<NonEmptyList<String>, Any> {
zip({ fail1() }, { fail2() }) { _, _ -> "No failures??" }
}.fold(
{ Assertions.assertEquals(nonEmptyListOf("hello!", "world!"), it) },
{ Assertions.fail<Unit?>("Unexpected success $it") }
)
}
test("Inlined functions should work exactly the same") {
eagerEffect<NonEmptyList<String>, Any> {
zip({ transformError { raise("hello") } }, { transformError { raise("world") } }) { _, _ -> "No failures??" }
}.fold(
{ Assertions.assertEquals(nonEmptyListOf("hello!", "world!"), it) },
{ Assertions.fail<Unit?>("Unexpected success $it") }
)
}
}
}
The second test is just the result of inlining fail1
and faill2
, which should be equivalent AFAIK. But the first test passes and the second one fails with
Expected :NonEmptyList(hello!, world!)
Actual :NonEmptyList(hello, world)
Jordi Pradel
12/22/2022, 9:57 AMfun <A> Raise<String>.transformError...
etc.simon.vergauwen
12/22/2022, 9:59 AMsimon.vergauwen
12/22/2022, 10:00 AMJordi Pradel
12/22/2022, 10:41 AM