typelevel / cats

Lightweight, modular, and extensible library for functional programming.
https://typelevel.org/cats/
Other
5.25k stars 1.21k forks source link

Add `attemptOption` to Alternative #2936

Open LukaJCB opened 5 years ago

LukaJCB commented 5 years ago
def attemptOption[A](fa: F[A]): F[Option[A]] =
  fa.map(_.some) <+> Option.empty[A].pure[F]
ybasket commented 5 years ago

@LukaJCB What use case(s) do you have in mind for that? Tried to find a good example for a doctest, but I can't come up with something "useful" - in fact, it has rather weird behaviour for some Alternative instances, for example List:

Alternative[List].attemptOption(Nil) // returns List(None)
Alternative[List].attemptOption(List(2)) // returns List(Some(2), None)

So it's pretty much fs2's noneTerminate on a List. Option itself seems also not very handy:

Alternative[Option].attemptOption(None) // returns Some(None)
Alternative[Option].attemptOption(Some(2)) // returns Some(Some(2))

I would see a use case for IO (equivalent to .attempt.map(_.toOption)), but as it has no Alternative instance, it doesn't matter here.

LukaJCB commented 5 years ago

If you're using mtl and have OptionT somewhere in your stack, this would allow you to materialize the option :)

E.g. something like ReaderT[OptionT[IO, ?], R, A]

ybasket commented 5 years ago

Maybe my head is a bit slow today - still can't figure out how you would use it. ReaderT[F, _, _] has an instance of Alternative iff F has and OptionT doesn't seem have any Alternative instance (only MonoidK). Can you maybe share a short code snippet that illustrates the use case?

LukaJCB commented 5 years ago

Oh that'd odd, I was sure OptionT had an Alternative instance 🤔

rmehri01 commented 4 years ago

Hi, I can try to help with this. Does an Alternative instance need to be added for OptionT then?