Hi, I am looking to short-circuit (with `Applicati...
# arrow
g
Hi, I am looking to short-circuit (with
Applicative
) + lazy call functions (with
BIO.defer
), so when
mapN
processes from left to right, first
raiseError
should halt the processing and rest of the functions shouldn’t be called, @pakoito helped me come-up with this approach, but I am facing 2 problems: 1. I am using BIO, but
raiseError
from
ApplicativeError
doesn’t return a
Kind2
, how can I get around it? 2.
IO.defer{..}
has this compiler error:
Type mismatch. Required: () → IOOf<String, ???> Found: () → Unit
for both my extension functions
raiseAnError()
and
justString()
Please help. Thanks.
Copy code
@Test
  fun `IO ApplicativeError 2`() {
        val product =
                IO.applicativeError<String>().mapN(
                        IO.defer { raiseSomeError() },
                        IO.defer { justString() }
                ) {}.fix().unsafeRunSyncEither()
        
        product.fold({ assertEquals("error", it) }, {})
    }

    private fun <S> ApplicativeError<S, String>.raiseSomeError(): Kind2<S, String, Unit> {
        println("raising an error")
        return raiseError("error")
    }
    
    private fun <S> ApplicativeError<S, String>.justString(): Kind2<S, String, Unit> {
        println("This should not be printed")
        return just("just")
    }
s
Hi,
I am using BIO, but
raiseError
from
ApplicativeError
doesn’t return a
Kind2
, how can I get around it?
That is correct, but you don’t need it to return
Kind2
.
Copy code
private fun <S> ApplicativeError<S, String>.raiseSomeError(): Kind<S, Unit> {
        println("raising an error")
        return raiseError("error")
    }
Should work just fine with
BIO
, your error is fixed to “String” here. Calling this function for
BIO
will return
Kind<IOPartialOf<String>, Unit>
so it returns
Kind2
when you use it with
BIO
.
S = IOPartialOf<String>
in this case. And the same principle applies for
justString()
.
g
Thanks @simon.vergauwen, I tried this, but still having compiler errors
Copy code
@Test
    fun `IO ApplicativeError 2`() {
        val product =
                IO.applicativeError<IOPartialOf<String>>().mapN(
                        IO.defer { raiseSomeError() },
                        IO.defer { justString() }
                ) {}.fix().unsafeRunSyncEither()
        
        product.fold({ assertEquals("error", it) }, {})
    }

    private fun <S> ApplicativeError<S, String>.raiseSomeError(): Kind<S, Unit> {
        println("raising an error")
        return raiseError("error")
    }
    
    private fun <S> ApplicativeError<S, String>.justString(): Kind<S, String> {
        println("This should not be printed")
        return just("just")
    }
message has been deleted
s
I’m taking a look at your snippet locally on
release/0.11.0
. Your
mapN
lambda is empty.
g
Yes
I can probably use
tupleN
s
The
ApplicativeError
instance for
BIO
still has
E
fixed to
Throwable
, and not parameterised over
E
. That’s why you saw that weird behavior 🙂 This snippet works fine.
BIO
is still WIP, especially the final touches like this.
👍 1
Its functionality should be stable, the API how ever is still WIP 🙂
g
Thanks a lot @simon.vergauwen, I think this problem is with IO as well in 0.10.5-SNAPSHOT
👍 1
s
This is not a problem for
0.10.5-SNAPSHOT
since there
IO
can only provide an instance fixed to
Throwable
.
You’re very welcome! All feedback and questions on
BIO
and its API are very welcome :)
Thanks for trying it out early!
g
🙂 thanks for such a welcoming community and mentorship
arrow 1
❤️ 1
@simon.vergauwen is there way to achieve such lazy calling with other ApplicativeError types, like EitherApplicativeError
Copy code
@Test
    fun `Either ApplicativeError`() {
        val product = Either.applicativeError<String>().run {
            tupledN(
                    raiseSomeError() ,
                    justString()
            ) 
        }.fix()
        product.fold({ assertEquals("error", it) }, {})
    }
This calls both the methods even though the first one raises error
s
Really?
g
ya
message has been deleted
@simon.vergauwen is that a bug?
I think I m eagerly calling both methods here, there should be some way like
<http://IO.de|IO.de>fer{}
For EitherApplicativeError
s
Either
doesn’t have the power to
defer
execution like
IO
can
g
Yes, no work around for that Case to get lazy calls?
s
No, you’ll have to use a data type which can implement
MonadDefer
such as
Eval
or
IO
or something similar
g
@simon.vergauwen Wish there is a combination of
MonadDefer + EitherApplicativeError/ValidatedApplicativeError
, just like
IO
(
MonadDefer
+
IOApplicativeError
). Otherwise the example [here](https://next.arrow-kt.io/docs/patterns/error_handling/#example--alternative-validation-strategies-using-applicativeerror) has a serious flaw, where all methods are called in failFast as well (Cc: @raulraja), which prevents using this approach in Prod. I tried desperately to mix both
IO.monadDefer()
and
Either.applicativeError()
but in vain 😞
s
Would your use-cases be met with
BIO
?
Since
BIO
is
IO
+
Either
it should serve your exact needs
Which is currently achievable by
EitherT<ForIO, E, A>
at a penalty of wrapping for syntax.
If you’re looking for a technique to accumulate
IO
errors, you can take a look at
Ank
which is the tool we use to validate our
KDoc
code snippets. It collects all the snippets and compiles without failing fast, that way we can output all the snippets/files that are broken instead of failing fast at the first broken snippet. Beside that
IO
already has the capabilities to fail-fast just like
Either
does.
Worst case, in anticipation of
BIO
you can use
Throwable
to model your
Left
side. Which causes fail-fast to happen in
IO
, this is also possible with a
sealed class
hierarchy if desired. However
BIO
will allow you to remove the
Throwable
constraint, and that will allow you to work with
BIO
as if it’s
suspend () -> Either<E, A>
.
Copy code
class DomainError : Throwable() {
  override fun fillInStackTrace(): Throwable = this
}
g
So just like the error handling example I quoted from Arrow docs, I can achieve the following using BIO? • Switch strategies (FailFast/Accumulation) for same code • FailFast doesn’t call subsequent methods on first error
s
Switch strategies (FailFast/Accumulation) for same code
I’d have to take a look how we can implement it should be possible and it’s a feature I’d like to have in Arrow Fx since it’s something a lot of people want/need out-of-the-box for doing validation over effects like db or network results.
FailFast doesn’t call subsequent methods on first error
IO
already does this for
Throwable
, and
BIO
does it for both
E
and
Throwable
so this is already achieved by both.
g
I’d have to take a look how we can implement it should be possible and it’s a feature I’d like to have in Arrow Fx since it’s something a lot of people want/need out-of-the-box for doing validation over effects like db or network results.
Thanks @simon.vergauwen I shall be eagerly waiting for this, to address my [requirement](https://github.com/overfullstack/ad-hoc-poly/blob/0093fa5c9a7c90790f5d353b2970bdae298757cd/validation-templates/src/main/kotlin/com/validation/rules/UserRules.kt#L35)
s
Awesome. I’ll try to make some time and I’ll take a look at your code. Maybe we can include a
Accumulation
strategy to
EffectValidator
and then we could include it in Arrow Fx if we want or at least in the documentation as a reference 🙂
👍 1
g
That’s great! 🙂
Hi @simon.vergauwen! sorry to bother u on a sunday! a gentle follow-up, did u get a chance to look at my code: https://github.com/overfullstack/ad-hoc-poly Thanks