typelevel / cats-effect

The pure asynchronous runtime for Scala
https://typelevel.org/cats-effect/
Apache License 2.0
2.03k stars 520 forks source link

Proposal: Rename Sync to Something Else #12

Closed alexandru closed 7 years ago

alexandru commented 7 years ago

Asynchronous versus Synchronous refers to the execution model and Asynchronous execution subsumes Synchronous execution as well.

We know that the Async type class has an async evaluation model because it's being set in stone by the register signature in its async function. Implementations are free to execute stuff immediately / synchronously of course.

Which is why Sync <: Async.

So problems with the current naming:

  1. the Sync type-class has nothing to do with synchronous execution ... in case of IO, the created instances might as well execute asynchronously, you never know, because IO is async
  2. there's no Sync <: Async relationship, no equivalence, no duality

To put things in perspective, an actual Sync type-class is the Comonad.

Alternatives:

mpilquist commented 7 years ago

Name wise, I'm on board with Suspendable or Deferrable or some variant.

I don't think Sync and Async should have a subtype relationship though. Note in FS2, the subtype relationship exists but inverted -- Suspendable >: Async.

alexandru commented 7 years ago

I don't want a subtype relationship either, that was to illustrate a point.

djspiewak commented 7 years ago

As a prefix, the signature of async can indeed be derived from delay, and it can be done in two different ways that both agree: strong CPS transformation (Steele and Sussman), and classical Curry-Howard Isomorphism. Well, rather I should say that the signature of async can almost be derived in these ways. Both methods generate the following signature:

def delay[A](thunk: => A): F[A]
def asyncIsh[A](k: (A => Nothing) => Nothing): F[A]

It's not hard to convince yourself that these two signatures are equivalent. However, we want async to provide more than just delay, since we want users of the signature to have the freedom to thread shift without throwing exceptions, which is something that the strict derivation directly rules out. Thus, we flip the booleans on the return types, producing ((Either[Throwable, A] => Unit) => Unit) => F[A].

So there is in fact an equivalence, just a modified one in the interest of use-cases.

I'm not married to the Sync name, but the primary reason I chose it (over Suspendable or similar) is to highlight this relationship. Also, both typeclasses are in the effect package, and so there's some precedent for this sort of thing (see: cats.functor.Invariant).

thee Sync type-class has nothing to do with synchronous execution ... in case of IO, the created instances might as well execute asynchronously, you never know, because IO is async

I think we need to be very clear about some terminology here. When I say "synchronous", I mean a computation which occupies and at least one stack frame and opaquely executes to completion, producing a result or an error. When I say "asynchronous", I mean a computation which is scheduled and may or may not have an active running state, and also may or may not eventually produce a result without holding a stack frame. Synchronous computations, by definition, consume at least one thread during their evaluation. They cannot be evaluated in any other way. Asynchronous computations are differentiated because they do not have this restriction (this is what converting the Nothings to Units buys us).

By these definitions, delay is in fact the very definition of synchronous computation. It cannot be asynchronous, because it's going to hold a thread from start-to-finish of its computation. Now, that thread may not be the "main" one, but that speaks to parallelism, not to asynchrony.

And so Sync defines the suspension of synchronous effects, to the same extend that Async defines the suspension of asynchronous effects. We define Effect to be the union of those two capabilities, together with the ability to asynchronously run as a side-effect (i.e. into IO).

Again, not married to the name, but this was the rationale for choosing it.

alexandru commented 7 years ago

@djspiewak I see.

Well, the naming is a little confusing to me as in my mind that name refers to something different and I'm thinking it will be confusing to other people. I guess the problem there is that by Async, I'm actually thinking of Effect, i.e. something that has an extract with an (A => Unit) => Unit signature.

If I'm the only one that has a problem with Sync, then you can ignore this proposal.

djspiewak commented 7 years ago

If I'm the only one that has a problem with Sync, then you can ignore this proposal.

As I recall, @mpilquist wasn't fond of the name either. :-) Let's get a little more input here.

alexandru commented 7 years ago

Can we reach a conclusion here?

Here's evidence that Sync will generate some confusion: April 24, 2017 11:46 PM 😏

djspiewak commented 7 years ago

Can we reach a conclusion here?

I want to hear from the other contributors as well. If we don't hear more opinions by end-of-week, I'll just make a decision.

Here's evidence that Sync will generate some confusion

I would argue that is evidence that people in general don't understand the distinction between "asynchronous" and "concurrent" (note the later characterization of Future). If anything, I would expect that Sync rather than Deferable (or similar) would help progress the community towards a better mental model of these terms. That's not to say Deferable is a misleading name, but rather Sync drives home the distinction with far greater precision.

alexandru commented 7 years ago

So if you care that much for Sync and others don't chip in, I'm not going to fight it 😃

At least it's short, so that's a plus and the way you defined Async it kind of makes some sense.

djspiewak commented 7 years ago

Alright, we're going to stick with the status quo for now. If this comes up again in the future though, we can revisit the decision.