roman-roman
roman-roman

Reputation: 2796

scala implicit in function type definition

I have following abstract class:

abstract class FieldProvider[+T: Writes](db: DB)(implicit i: RequestAction, j: ExecutionContext) {}

and following implementations:

class LengthProvider extends FieldProvider ...

object LengthProvider extends ((DB) => LengthProvider) {
  def apply(v1: DB): LengthProvider = new LengthProvider(v1)
}

class WidthProvider extends FieldProvider ...

object WidthProvider extends ((DB) => WidthProvider) {
  def apply(v1: DB): WidthProvider = new WidthProvider(v1)
}

The reason why I have these apply methods is because I need following configuration map:

val providers: Map[String, ((DB) => FieldProvider)] = Map(
 "length" -> LengthProvider,
 "width"  -> WidthProvider
)

So that I can initialize the providers by the string containing their name:

providers("length")(db) // returns a new instance of LengthProvider

Now, my problem is that all these providers constructors require two implicit variables. But I don't know how to include it into the function definition (DB) => FieldProvider. So, essentially, the apply method signature should be something like (DB)(implicit RequestAction, ExecutionContext) => FieldProvider, but I don't know if there is a correct syntax for what I'm trying to do.

I could also give up and pass them explicitly:

object WidthProvider extends ((DB, RequestAction, ExecutionContext) => WidthProvider) {
   def apply(v1: DB, v2: RequestAction, v3: ExecutionContext): WidthProvider = new WidthProvider(v1)(v2,v3)
}

But then I'll have to pass them explicitly elsewhere, instead of providers("length")(db), I'd have to write providers("length")(db, implicitly[RequestAction], implicitly[ExecutionContext]), which doesn't feel right.

Upvotes: 1

Views: 310

Answers (2)

VonC
VonC

Reputation: 1323005

So, essentially, the apply method signature should be something like (DB)(implicit RequestAction, ExecutionContext) => FieldProvider, but I don't know if there is a correct syntax for what I'm trying to do.

Note that you might directly get (in a future Scala 2017 version) an implicit function type.

See pull request 1775, by Odersky himself.

Let’s massage the definition of f1 a bit by moving the last parameter section to the right of the equals sign:

def f1(x: Int) = { implicit thisTransaction: Transaction =>
  thisTransaction.println(s"first step: $x")
  f2(x + 1)
}

The right hand side of this new version of f1 is now an implicit function value.
What’s the type of this value?
Previously, it was Transaction => Int, that is, the knowledge that the function has an implicit parameter got lost in the type.

The main extension implemented by the pull request is to introduce implicit function types that mirror the implicit function values which we have already.
Concretely, the new type of f1 is:

implicit Transaction => Int

Just like the normal function type syntax A => B, desugars to scala.Function1[A, B], the implicit function type syntax implicit A => B desugars to scala.ImplicitFunction1[A, B].
The same holds at other function arities. With dotty’s pull request #1758 merged, there is no longer an upper limit of 22 for such functions.

Upvotes: 0

flavian
flavian

Reputation: 28511

Let's assume FieldProvider has 2 methods that need the implicits. The more convenient way to avoid duplication is to pass them as constructor level implicit arguments and then all the internal methods in FieldProvider can "share" them.

However, this won't help in your current class tree, so to fix this, instead of doing:

abstract class FieldProvider()(implicit param1: X1..) {
  def test: T = param1.doSomething
}

Simply move the implicit to method level such that you can provide it at a time different than extending the constructor.

abstract class FieldProvider() {
  def test()(implicit param1: X1): T = param1.doSomething
}

Upvotes: 2

Related Questions