Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add attemptNarrow to ApplicativeErrorOps #2863

Merged
merged 4 commits into from
Oct 23, 2019

Conversation

marcodippy
Copy link
Contributor

Hello, I already copy-pasted this function in a few places in my codebases, so I thought it could be useful to have it in cats.

Example use case (assuming F : ApplicativeError[F, Throwable]):

def authenticate(r: Request[F]): F[Unit] = ???

val authentication: Kleisli[F, Request[F], Either[AuthenticationException, Unit]] =
  Kleisli { request => authenticate(request).attemptCase[AuthenticationException] }

@codecov-io
Copy link

codecov-io commented May 30, 2019

Codecov Report

Merging #2863 into master will decrease coverage by 1%.
The diff coverage is 100%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #2863      +/-   ##
==========================================
- Coverage   94.18%   93.17%   -1.01%     
==========================================
  Files         368      372       +4     
  Lines        6948     7182     +234     
  Branches      308      198     -110     
==========================================
+ Hits         6544     6692     +148     
- Misses        404      490      +86
Flag Coverage Δ
#scala_version_212 93.52% <100%> (?)
#scala_version_213 90.85% <100%> (?)
Impacted Files Coverage Δ
core/src/main/scala/cats/ApplicativeError.scala 100% <100%> (ø) ⬆️
.../src/main/scala/cats/syntax/applicativeError.scala 100% <100%> (ø) ⬆️
core/src/main/scala/cats/instances/parallel.scala 0% <0%> (-100%) ⬇️
core/src/main/scala/cats/syntax/parallel.scala 62.5% <0%> (-17.5%) ⬇️
kernel/src/main/scala/cats/kernel/Monoid.scala 72.72% <0%> (-16.17%) ⬇️
...ernel/src/main/scala/cats/kernel/Semilattice.scala 85.71% <0%> (-14.29%) ⬇️
core/src/main/scala/cats/instances/sortedMap.scala 84.74% <0%> (-12.76%) ⬇️
core/src/main/scala/cats/data/NonEmptyChain.scala 85.39% <0%> (-11.93%) ⬇️
core/src/main/scala/cats/instances/sortedSet.scala 88.23% <0%> (-11.77%) ⬇️
core/src/main/scala/cats/data/Op.scala 77.77% <0%> (-9.73%) ⬇️
... and 103 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 0209330...f6e9228. Read the comment docs.

@@ -83,6 +86,9 @@ final class ApplicativeErrorOps[F[_], E, A](private val fa: F[A]) extends AnyVal
def attempt(implicit F: ApplicativeError[F, E]): F[Either[E, A]] =
F.attempt(fa)

def attemptCase[EE](implicit F: ApplicativeError[F, E], tag: ClassTag[EE], ev: EE <:< E): F[Either[EE, A]] =
F.recover(F.map(fa)(_.asRight[EE])) { case e: EE => e.asLeft[A] }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given that extra inter-dependencies could lead to circular dependencies, perhaps we can by-pass the either syntax and use this not much longer alternative:

Suggested change
F.recover(F.map(fa)(_.asRight[EE])) { case e: EE => e.asLeft[A] }
F.recover(F.map(fa)(x => Right[EE, A](x))) { case e: EE => Left[EE, A](e) }

@kailuowang
Copy link
Contributor

I think this could be useful although I am not quite sure about the name. I am thinking attemptNarrow or narrowAttempt?

@kailuowang kailuowang changed the title add attemptCase to ApplicativeErrorOps add attemptNarrow to ApplicativeErrorOps Jun 21, 2019
@marcodippy
Copy link
Contributor Author

@kailuowang renamed to attemptNarrow 👍

kailuowang
kailuowang previously approved these changes Jun 21, 2019
Copy link
Contributor

@kailuowang kailuowang left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

@@ -83,6 +85,9 @@ final class ApplicativeErrorOps[F[_], E, A](private val fa: F[A]) extends AnyVal
def attempt(implicit F: ApplicativeError[F, E]): F[Either[E, A]] =
F.attempt(fa)

def attemptNarrow[EE](implicit F: ApplicativeError[F, E], tag: ClassTag[EE], ev: EE <:< E): F[Either[EE, A]] =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we need a classtag here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

EE would be erased and we wouldn't be able to pattern match on it otherwise @tkroman

@kailuowang kailuowang requested a review from LukaJCB August 2, 2019 16:36
LukaJCB
LukaJCB previously approved these changes Aug 2, 2019
Copy link
Member

@tpolecat tpolecat left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would like to see a test with a parameterized type, also demonstrating non-compilation for things like narrowing to List[Int] when it's actually List[String]. Since we're skating close to erasure.

@marcodippy marcodippy dismissed stale reviews from LukaJCB and kailuowang via 3767ca3 August 2, 2019 21:57
@marcodippy
Copy link
Contributor Author

@tpolecat I added the test, let me know what you think

kailuowang
kailuowang previously approved these changes Aug 3, 2019
@marcodippy
Copy link
Contributor Author

hey @kailuowang, is there anything I can do to get this merged?

LukaJCB
LukaJCB previously approved these changes Oct 22, 2019
@kailuowang kailuowang dismissed their stale review October 23, 2019 01:06

one more thing

@kailuowang
Copy link
Contributor

Hi @marcodippy sorry about the delay here, but can I ask one more thing? Now that we dropped 2.11 from master branch, we can add this method to the typeclass itself and have the syntax delegate to it. Would you make that change?

@marcodippy
Copy link
Contributor Author

no problem @kailuowang, I just pushed the change :)

Copy link
Contributor

@kailuowang kailuowang left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

@kailuowang kailuowang merged commit f414c1d into typelevel:master Oct 23, 2019
@travisbrown travisbrown added this to the 2.1.0-RC1 milestone Nov 5, 2019
gagandeepkalra added a commit to gagandeepkalra/cats that referenced this pull request Feb 9, 2020
rtyley added a commit to scanamo/scanamo that referenced this pull request Jul 23, 2024
The original implementation of the code to expose the `ConditionalCheckFailedException`
was introduced with PR #212 in May 2018, but I'm not sure why the multi-line implementation
was necessary - there's a discussion in PR #212, but I don't quite follow it: #212 (comment)

The Cats `recover` functionality I'm using here was added with `ApplicativeError` in
typelevel/cats#812 in January 2016, so it would have been available
for PR #212. Perhaps it was some consequence of us being on AWS SDK v1 at that point, and
having to use `AsyncHandler`?

This implementation is definitely much more similar to ones we've used elsewhere, ie
`ScalaFutureAdapter` and `PekkoInterpreter`.

If we had a `ClassTag` we could go even smaller and use `attemptNarrow`, which was added
to Cats with typelevel/cats#2863 in May 2019 - but even without that,
the implementation is much smaller.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants