-
Notifications
You must be signed in to change notification settings - Fork 535
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
CE3: ExitCase.Completed #643
Comments
I used to have this concern as well, but @djspiewak reckons it's not an issue. You have
Conversely, if ExitCase has
This is interesting, I'll have a look at the code for that. All in all, |
I've reread the Bracket issue, and according to this #113 (comment) the |
It's very much necessary for both The code right now is a little convoluted due to another issue, but I think you can see it: This is because we are implementing
This means that in case of a Having a |
@SystemFw Also for streams:
This is where you might be right, the details are a little fuzzy, but if we implement |
Btw I think |
I see the problem for Anyway, my main point for wanting that |
Can you explain what the issue with continual is? Have you written about it somewhere? Of course I would be interested in continual. 🙂 |
So, this is the polymorphic def continual[F[_], A, B](fa: F[A])(f: Either[Throwable, A] => F[B])
(implicit F: Concurrent[F]): F[B] = {
import cats.effect.implicits._
import scala.util.control.NoStackTrace
Deferred.uncancelable[F, Either[Throwable, B]].flatMap { r =>
fa.start.bracket( fiber =>
fiber.join.guaranteeCase {
case ExitCase.Completed | ExitCase.Error(_) =>
fiber.join.attempt.flatMap(f).attempt.flatMap(r.complete)
case _ => fiber.cancel >>
r.complete(Left(new Exception("Continual fiber cancelled") with NoStackTrace))
}.attempt
)(_ => r.get.void)
.flatMap(_ => r.get.rethrow)
}
} As you can see, it's short but pretty complex.
it seems like you could use def guaranteeCase[A](fa: F[A])(finalizer: ExitCase[E] => F[Unit]): F[A] But then you notice that But as I said, this is not a blocker: we could implement |
I see the problem you’re getting at, Alex. OptionT is a great example. Need to think about this more… |
The view in question comes from a somewhat more abstract look at things. Maybe the viewpoint isn't useful, but just to explain the thought process a bit better… Cats Effect's abstractions form a calculus which defines the basis for functional effectful runtimes in Scala. Every calculus is predicated on a series of axioms, which are nothing more than statements we simply assert to be true. All functors define a All of this is arbitrary and, on some fundamental level, meaningless. Cue Plato in 3...2...1... Anyway, bespoke axioms are a necessary fact of useful philosophical (and by extension, mathematical) systems, because you can't derive your own axioms in a consistent system (see: Gödel). However, they're also profoundly annoying because they are so arbitrary. I'm always a lot more comfortable when I conclude something to be factual if I built that conclusion upon logical inferences derived from other facts. I'm much less confident if I say something is factual "because I say it is". All Linking this back to Cats Effect… One of my goals with CE3 was to reduce the axiomatic space in the calculus of effects. The more functionality we have which is the way it is "because we say it is", the less confident we can be that we actually made something meaningful. Minimizing the calculus to a smaller set of axioms which compose to produce a system consistent with what we want to express is a major goal of nearly all of mathematics. (we can quibble about why minimization is such a major goal, but… it is, and all of modern systematic thought is built upon it) Concretely in Scala, this means trying to have more and more of Cats Effect's functionality expressed in terms of fewer and fewer abstract functions on the typeclasses. It means being able to derive more and more laws (e.g. the ones relating Of course, there is a back and forth here. We can't just design in a vacuum sealed at the top of our Ivory Tower. Axioms which cannot be implemented efficiently by concrete datatypes are bad axioms, and their existence would need to be very seriously justified to merit inclusion in the calculus. Axioms which indirectly preclude a swathe of desirable implementations or use-cases are also bad. Conversely, axioms which indirectly force efficient or safe implementations, or which enable desirable use-cases, are very good axioms. A particularly strong axiom is one which applies its implications to numerous orthogonal desirable attributes. I feel like I'm rambling… The point being that there's some give and take here. The core calculus of Cats Effect cannot be designed without regards to how it will be implemented and how it will be used, but conversely we also should not make changes to that calculus solely based on how implementations or use-cases generally work today. Instead, we should try to take those implementations and use-cases and work backwards to the first principles which give rise to them, and from that kernel of truth see if we can derive a clean axiomatic framework into which it fits. The contrapositive of this is taking undesirable implementations or use-cases that we see today, and work backwards to the first principles which allow us to form a clean axiomatic framework which precludes them. An example of the latter would be the very common mistake that almost everyone has made at least once with This is the kind of thing I'm talking about. Now, not every use-case in the CE3 API is quite this clean, but hopefully the thought process and design intent is clear. Or at least, as clear as I can make it. Sorry for the vast prose. So how does all of this relate to
If you ignore Unfortunately, you're completely right that Technically, As a note, these sorts of type level shenanigans tend to be very annoying for end-users to deal with, due to the vastly more complicated types and awkward syntax, so I don't think we should go down this road without significant justification. It also (analogously) makes the type of |
I think I've solved this, now that I had time to loop back to things. The new sealed trait ExitCase[+F[_], +E, +A] extends Product with Serializable
object ExitCase {
final case class Completed[F[_], A](fa: F[A]) extends ExitCase[F, Nothing, A]
final case class Errored[E](e: E) extends ExitCase[Nothing, E, Nothing]
case object Canceled extends ExitCase[Nothing, Nothing, Nothing]
} I don't like complicating the kind even further, but I think it carries its weight. This should solve the problem with both def join: F[ExitCase[F, E, A]] Which implies that if you want the current CE2 semantics, you can do the following: f.join.flatMap(_.fold(never, raiseError(_), fa => fa)) Laws will guarantee that implementors must represent errors and cancelation within All in all, this is more general than the current state in master and doesn't require users to manually manage closed |
Ah, interesting. Can it work for If it does, then it's not bad. |
It definitely works for EitherT and it should work for streams. It’s a bit bizarre though since you would need to either determine the exit result for the entire stream before running it, or the outer effect would have multiples and you would return multiple cases (as I originally designed for it). I suspect that the laws will force this, if you apply it to a stream. |
The
ExitCase
in the CE 3 proposal is this:And I worry specifically about this case:
The reason for why the current
Completed
does not havea: A
in it is for it to be able to be used:flatMap
if you want to trigger stuff in case of normal termination, you don't needbracket
to act as aflatMap
as wellEitherT[IO, E, *]
, theCompleted
case can be used to signal something other than successful completion ... ifError
signals aThrowable
, thenCompleted
does NOT necessarily signal success, it could simply be the result of anE
error that happened (in the context ofEitherT
) ... which is why we can implementSync[EitherT[F, E, *]]
for anyE
This was already discussed on the PR for
Bracket
and I admit it's very noisy so hard to spot — but @LukaJCB should be aware of the problem as well:#113 (comment)
The CE 3 proposal says this:
This view is not useful and is where we disagree. I don't see
Completed
as being the counterpart topure
orErrored
as the counterpart toraiseError
.I view
Completed
as beingUnit
(aka termination) andError | Canceled
as beingNothing
. No more, no less.The text was updated successfully, but these errors were encountered: