How can I `mapLeft` in the new `EffectScope`? ```c...
# arrow
t
How can I
mapLeft
in the new
EffectScope
?
Copy code
context(EffectScope<Throwable>)
fun count(): Int = TODO()

sealed interface DomainError

context(EffectScope<DomainError>)
fun logic(): Int {
  // I want to map Throwable to DomainError here
  return count()
}
s
Hey @thanh, Currently your best bet is to capture it into an
effect
and then call
fold
. Or turn it into an
Either
and call
mapLeft
and rebind.
Copy code
effect<Throwable, Int> {
  count()
}.fold({ shift(transform(it)) }, { it })
We've been working on new APIs to cover MonadError/ApplicativeError, etc. Not sure if you've seen these APIs, https://github.com/arrow-kt/arrow/pull/2797. I'm planning to port them back to 1.x.x soon. Any feedback would be great. This would replace everything you find under these error APIs, since they cover all use-cases.
Additionally, I've also been working on a new API for debugging and tracing purposes. Any feedback here would also be great. Currently it's still in the process of API design since the functionality works already. https://github.com/arrow-kt/arrow/pull/2816 This tracing support will offer tracing stacktraces for
shift
and thus also
bind
,
ensure
,
ensureNotNull
, ... everything in the Effect DSL and everything that is build on top ๐Ÿฅณ
t
oh, that looks great! I'll check that. Do we have a snapshot version of Arrow 2.0?
s
There is a SNAPSHOT for Arrow 2.0, at least we've enabled it. I haven't tried it myself yet.
You were wrapping some side-effects in
count()
? The API designed for this is:
Copy code
context(EffectScope<String>)
fun count(): Int =
  catch({ TODO() }) { ex: IllegalStateException ->
    shift(ex.message)
  }
And here I am using the
reified
variant to select
IllegalStateException
as a partial function ๐Ÿ˜‰
t
So in
count
, there is some
Error
, but depend on each
logic
function, I want to map it to some different
DomainError
. That's why mapLeft here is so convenient.
s
Okay, if you're mapping between typed errors you want to use
recover
.
Copy code
context(Shift<E>)
suspend fun count() = 1

context(Shift<E2>)
suspend fun logic(): Int =
  recover({ count() }) { e: E ->
    shift(e.toE2())
  }

fun E.toE2(): E2 = TODO()
recover
allows you to do what
mapLeft
does but in a DSL style
It will also exist as an extension on
Effect
, derived from the DSL method ๐Ÿ˜‰
So
catch
for
Throwable
and
recover
for typed errors, and
recover
allows you to transform the error type.
You can see them as the uber functions from
MonadError
, all use-cases can be derived from them.
t
thanks Simon, I understand those functions. But still seems a bit more work than before ๐Ÿ˜‚ .
s
It requires you to call
shift
yes ๐Ÿ˜…
How would you use
mapLeft
otherwise?
t
I meant before
ScopeEffect
, with
EitherScope
, we can just mapLeft directly, right?
s
You can do:
Copy code
val x: Either<String, Unit> = ...
val y: Either<Int, Unit> = 
  x.mapLeft { it.length }

val x2: Effect<String, Unit> = ...
val y2: Effect<Int, Unit> =
  x2.recover { shift(it.length) }
I would say the difference is more in the passing of values vs DSL style of coding. When working in DSL style you need to "wrap" in DSL blocks, just like you do with KotlinX.
Copy code
withTimeout
coroutineScope
withContext
...
All these Kotlin APIs are DSL blocks, rather than extension methods like we used to do before or often see in Scala.
t
Thanks Simon, I'll experiment with your suggestions.
s
If you have more feedback it would be really great to hear ๐Ÿ™
This is going to be important to get right for Arrow 2.0
t
yeah, definitely, I'll may try Arrow 2.0 soon ๐Ÿ˜„
s
Hey stumbled upon this by chance, and I wonder, what is the final design for this for 2.0?
s
The final design is what is in 1.2.0, so basically everything that I proposed in this thread.
recover
for types errors, and
catch
for exceptions.
thank you color 1
๐Ÿ‘Œ 1
169 Views