2

Is it more idiomatic Scala to pass in ExecutionContext per method like

class Foo {
    def bar(a: Int, b: Int)(implicit ec: ExecutionContext): Future[Int] = {
        Future(a + b)
    }

    def baz(a: Int, b: Int)(implicit ec: ExecutionContext): Future[Int] = {
        Future(a - b)
    }
}

or better to pass in ExecutionContext per class like

class Foo(implicit ec: ExecutionContext) {
    def bar(a: Int, b: Int): Future[Int] = {
        Future(a + b)
    }

    def baz(a: Int, b: Int): Future[Int] = {
        Future(a - b)
    }
}

Is one style usually more preferred in Scala world because it causes less surprises, is easier to read, or for other reasons? Please give some references if possible.

krismath
  • 1,879
  • 2
  • 23
  • 41
  • 1
    An example for the second pattern are asynchronous database drivers (like ReactiveMongo). These usually have an internal ExecutionContext that you configure when you open the connection. And then you call query methods on the driver, getting back a Future, without specifying the EC on each call. But when you then want to post-process the result (by `map`ping over the Future from the DB), you have to provide your own EC again for that. – Thilo Nov 08 '19 at 10:53
  • 1
    This whole thing gets a lot cleaner if you move to an effects library (like ZIO, Monix or Cats Effects). Then you get to combine "tasks" and only at the very end have to provide the execution environment (so no implicit EC being passed all over the place). – Thilo Nov 08 '19 at 10:56
  • @Thilo Seems that you have a good answer. Can you put that as an answer, including the common usage of each style, esp. in existing libraries? – krismath Nov 08 '19 at 11:03
  • 1
    you can read about my recommendations here: https://viktorklang.com/blog/Futures-in-Scala-protips-1.html – Viktor Klang Nov 08 '19 at 12:49

1 Answers1

5

The two options have different semantics so neither is idiomatic.

The first option allows the caller to specify the execution context at call time and allows different contexts to be used for different calls.

The second option requires that the same context is used for all calls.

The choice depends on the semantics that you want for your class and methods.

Tim
  • 26,753
  • 2
  • 16
  • 29
  • Should the same context be used across all calls? I honest don't understand why would I have multiple ExecutionContexts. – krismath Nov 08 '19 at 10:19
  • Different caller can use the same stateless/immutable class from different context – cchantep Nov 08 '19 at 10:27
  • 2
    You almost certainly want to have a separate ExecutionContext for anything blocking, and on top of that it may make sense to have different ones for different sub-systems (Database, external HTTP calls, internal service calls) so that you can configure them separately (e.g. different number of threads) – Thilo Nov 08 '19 at 10:45
  • So the first one is more recommended in general? – krismath Nov 08 '19 at 11:01
  • It really depends on the case. For `Future(a+b)` you might just as well use the default global execution context (which is *not* recommended in general). Always leaving it up to the caller with the implicit is the most common practice (just a bit ugly with the boilerplate). – Thilo Nov 08 '19 at 11:04