Upates to scala.concurrent.Future wrt breaking changes

Allowing map to use a Future’s ExecutionContext violates encapsulation. An object might be performing computations using its own, private ExecutionContext, and exposing the results of those computations in Futures. If map can/will use that private ExecutionContext, then it exposes what was meant to be private, and may violate invariants of the ExecutionContext (for example, it may be a java.util.concurrent.Executors.newSingleThreadExecutor(), and allowing outside computations could slow down computations the object is doing).

1 Like

Hi @viktorklang,

I’m very happy to hear that Future is receiving a performance upgrade.

Future is indeed ubiquitous and we really should give more credit to its design, a very good design if you take a look at what other platforms have, my favorite past time of late being to complain about JS’s Promise.

On cancellation, your reasoning is good, which is why I wouldn’t include a CancelableFuture by default in the standard library —in the case of Monix the CancelableFuture was built for usage with Task or with Observable. In that context you get cancelable future refs at the “edges of the FP program”, when you call runAsync (our equivalent for Haskell’s unsafePerformIO), which ideally happens only once in main (or as few times as possible) within the program.

I do think that cancellability leads to more safety due to having the ability to close resources and avoid leaks on race conditions, which are inevitable with concurrency — the simplest example is being able to describe a timeout operation that does not leak. But cancelling a future does indeed mean that the developer has to encapsulate the usage of that Future and not share it with other consumers without due diligence. So point well taken.

Personally I’m against including a Task-like abstraction in the stdlib. This is because including such an abstraction in stdlib involves stagnation and discourages the use of any alternatives.

For Future I’m pretty sure that you had plenty of prior work to analyze and decide on the implementation we have now. For Task however we are still deciding on what the best encoding is, while having a continuous debate about responsabilities / boundaries, i.e. should it take care of concurrency concerns or not, which really boils down to if it should it be cancelable or not and whether it should have a notion of some underlying “execution context” or not — and there are very good arguments on each side of the fence.

So as long as the dust doesn’t settle on what the best approach is, including it in the standard library would do more harm than good. Plus such an abstraction really works best within the context of an FP library like Cats or Scalaz. So if we don’t get the Monad type class to go along with it, which would open another can of worms, maybe it’s best to leave it out for now.

1 Like

On this note, I don’t think that Future had as much prior (at least it wasn’t any more prior work considering the current place where Monix Task is). In fact I would argue that Monix Task is probably more developed compared to when Future was included into the Scala stdlib (as evidenced by a lot of the low hanging fruit performance improvements which haven’t been done yet).

I also think its quite unquestionable that having a ubiquitous async type Future has overall been a tremendous benefit to Scala. Even if the implementation is not perfect, its inclusion has bolstered a massive ecosystem and has also greatly helped Scala.js interopt with Javascript (i.e. Future maps very nicely in the Javascript ecosystem and we can use the same async type on both platforms with almost identical behaviour).

I guess I am of the unpopular opinion around here, but I think there should be a standard lazy async type (lazy version of Future, i.e. Task) in the standard library. In fact not having this is what has caused so much chaos and confusion in the Scala ecosystem particularly for people who want a purely functional solution. There have been many competing types of Task, entire libraries have had to be redesigned because of this flux (i.e. see Http4s going from scalaz task to FS2). While its true that competition is healthy because we eventually find a good solution, I think we have already passed a point where the solution/s is good enough,.

I however digress because there is a move (with good reason) to put stuff out of the stdlib and into the SP (and Future) may end up following suite (in any case my original argument would still apply, a lazy Task would reside in the same place where Future ends up being). There are however technical issues which have been stated, ie. the Scala stdlib no having typeclasses for many of the purely functional types which current Task relies on (now using cats for this functionality). Furthermore the scope would have to be agreed upon, one of the reasons I believe that Future was so successful is that it was quite limited in what it did. It didn’t handle streaming, nor did it have interfaces for things like Observable. It was strictly just representing an async computation with an interface for fairness/execution context and safety regarding stacktraces. We then have other libraries building ontop of this (akka-stream/akka-http and Monix Task) which is actually the situation we want.

If there was a lazy type of Task that would be included in SP/stdlib, i think it would have to have a similar restricted scope.

@mdedetrich you gave the example of FS2 which is undergoing a painful transition to Cats.

It’s not necessarily a good example for the necessity of having a Task in the standard library, because that transition to Cats would have happened anyway and it would have been painful anyway, because the availability of a Task isn’t the only problem that a library like FS2 has.

I think we have a problem of causality — if we have both Cats and Scalaz in the ecosystem with different implementations and community management, that’s because we cannot agree on how to best do things.

By including something in the standard library you’re basically forcing users to agree on one true solution. Sometimes that’s valuable, however that’s also the Microsoft-mentality of blessing implementations and of discouraging community-provided solutions.

And while that might be desirable, if we take a close look at the landscape, we can notice that the metaphorical Bazaar has been winning for quite some time against the Cathedral — think Java / JVM versus C# / .NET, Clojure versus Scala, JavaScript vs everything else.

Yes, Future is a success. The standard collections are a success. But this sampling is biased, because if we take a look at things like JSON / XML parsing, the landscape is filled with the corpses of failed implementations in standard libraries that nobody wants, but have to endure due to legacy code.

Speaking of which I don’t like the idea of the Scala Process that much — being basically about making the standard library more modular and expanding it with what are considered to be the needed batteries, which is good for adoption, but a SP project is stdlib nonetheless, with all the downsides that brings.

Personally I don’t believe in batteries included and pretty much ended up hating it in every language that was advertised as having batteries included, like Python or Java.

A note. The Scala Platform process is compatible with the Bazaar idea you mention. The Scala Platform provides libraries that want to help all Scala developers. There can very well be a Typelevel Platform, or a Scalaz Platform, or Your Organization’s Platform if you want, and they can complement themselves.

The Scala Platform modules are community-driven and can be adapted and evolved over time, unlike the official Scala Standard library (or soon to be known as Scala Core in 2.13). In fact, no developer employed by Lightbend or EPFL provides support for the modules of the Scala Platform. We want to create solid ecosystems around the libraries that join the Platform, and also encourage companies to fund/sponsor them.

I don’t want to gear this discussion to another matter, but a clarification here is important. If you want to continue discussing this, please consider opening a new thread. :smile:

Some issues were technical, but a lot were also politicial/NIH syndrome. There isn’t for example really significant differences between fs2 Task and scalaz7 Task. In any case I don’t really think its constructive to go over the whole cats/scalaz debacle.

It has also failed miserably in a lot of other environments, i.e. have a look at Haskell (3 different string types, which all library authors have to target because its a single string type) or at Scala having ~7 different type of JSON AST’s (i.e. have a look at slick-pg and how many JSON libraries it needs to target GitHub - tminglei/slick-pg: Slick extensions for PostgreSQL)

In Scala its actually an even worse problem because we do dependency management via binaries, and because of the tree your dependencies form its really easy to get into dependency hell due to binary compatibility.

These really aren’t good examples.

The JSON library was included as a demo to test parser combinators, it wasn’t really included as a goal of being a “good JSON library”.

WRT to xml, I am not sure if you are talking about XML literals or the scala xml library. If you are talking about the latter, considering how complicated XML is it actually was quite a good library especially for its time.

In any case it comes down to how critical functionality is, and I believe async primitives (at least if we are dealing with modern programming where this is very common) there should be some basic implementation that is good for general use case usage.

Note that as @jvican has said, this doesn’t prevent community contributions nor does it mean that the SPP doesn’t support a bazaar approach. In any case this is getting a bit off topic and should be discussed in another thread