Arrow 2.2.1.1 is out! <https://arrow-kt.io/communi...
# arrow
a
arrow intensifies 15
o
Very cool! Tried it out immediately! I found one method,
ensureNotNull
where
@IgnoreableReturnValue
is missing, though! 😉
It's in package
package arrow.core.raise.context
- I am talking about the Raise context extensions.
The next question: often I am using
mapOrAccumulate
or
zipOrAccumulate
in conjunction with verification and collection of errors. So the functions I call all return
Unit
. Of course the mapped/zipped result of such a call is irrelevant, because it's just
List<Unit>
or
Unit
. With the new return value checker, those two functions also require the return value to be used. Is there any way to make an exception if the result is just
Unit
? Or does Arrow have specific functions I didn't find yet that do the accumulation part without actually mapping/zipping?
On that note:
public inline fun <Error, A, B> Raise<NonEmptyList<Error>>.mapOrAccumulate(
is still missing in Raise context extensions... 😉
I created these functions myself to bridge and have a variant with ignorable result:
Copy code
@RaiseDSL
context(raise: Raise<NonEmptyList<Error>>)
inline fun <Error, A, B> mapOrAccumulate(
    nonEmptyList: NonEmptyList<A>,
    @BuilderInference transform: RaiseAccumulate<Error>.(A) -> B,
): NonEmptyList<B> {
    contract { callsInPlace(transform, AT_LEAST_ONCE) }
    return raise.mapOrAccumulate(nonEmptyList, transform)
}

@RaiseDSL
context(raise: Raise<NonEmptyList<Error>>)
inline fun <Error, A> mapOrAccumulate(
    nonEmptyList: NonEmptyList<A>,
    @BuilderInference transform: RaiseAccumulate<Error>.(A) -> Unit,
) {
    contract { callsInPlace(transform, AT_LEAST_ONCE) }
    val _ = raise.mapOrAccumulate(nonEmptyList, transform)
}
a
the one for
ensureNotNull
, it should be added, definitely about the
mapOrAccumulate
and similar, two points: 1. the Kotlin team is working on some way of saying "ignore the value if the function I pass is ignorable", but it's not there yet 2. for the time being, you should use
forEachAccumulating
if you don't care about the values
♥️ 1
o
Yes, my biggest pain is more
zipOrAccumulate
which is necessary when the functions that could raise errors are not uniform. Let's say I have some validators that are able to raise
Nel<String>
as an error. I then call them using:
Copy code
context(Raise<Nel<Error>>)
fun validateA() {}

context(Raise<Nel<Error>>)
fun validateB() {}

zipOrAccumulate(
  { validateA() },
  { validateB() }
) { _, _, -> }
By the way: I always wondered why there is no
RaiseNel
type, equivalent to
EitherNel
?
a
I don't fully understand this use case: • if you're using
Raise
, then you should get
Unit
back, which is implicitly ignorable • if you're using
Either
, then you get a
Either<Nel<Error>, Unit>
back, and then the warning is correct: you should do something with that value
o
No, you misunderstood. This is about a general zipFunction to accumulate errors only. This has nothing to do with the return value checker.
the purpose of my usage of
zipOrAccumulate
is basically the
accumulate
part of it. There's nothing to zip because everything is just
Unit
.
I took this from the internal workings of
zipOrAccumulate
, I need this:
Copy code
accumulate {
     accumulating { action1() }
     accumulating { action2() }
     accumulating { action3() }
     accumulating { action4() }
     accumulating { action5() }
}
But without the experimental raise accumulate stuff and tbh I am rather confused about the two similarly named functions stacked within each other. Why is one called accumulate and the other accumulating?
a
the idea is that you "run
action1
accumulating errors"
although maybe in your case you may want
Copy code
accumulate {
  action1().bindOrAccumulate()
  action2().bindOrAccumulate()
  ...
}
o
The problem is that both accumulate scope and accumulating function are marked experimental API, while zipOrAccumulate is not. So I prefer to stick with the zip function for now. I just wrote my own wrapper that ignores the Unit results and supplies the zip code block to do nothing at all.
y
The naming for
accumulate
and
accumulating
is not perfect, I agree. The intuition is that
accumulate
is your entry point into the
Accumulate
DSL, while
accumulating
is part of the
Accumulate
DSL, and is used to "soften" errors. The basic structure of
accumulate
and
accumulating
will continue to work. In fact, there's no intention to remove this DSL at all. The exact design and semantics may change, however. Basically, if it shows up in a non-experimental
inline
function, we guarantee binary compatibility for it. Source compatibility, not so much, because the DSL is evolving. However, the source pattern of
Copy code
accumulate {
  accumulating { ... }
  accumulating { ... }
  accumulating { ... }
}
is incredibly unlikely to change. It's the core of the
Accumulate
dsl, and that DSL is something we want to ship fully eventually. Worst case scenario, maybe we change the names to be more understandable, but there's always be some
accumulate {}
equivalent and some
accumulating { }
equivalent