How to wrap a method having implicits with another method in Scala?
Asked Answered
W

1

4

I have a method with implicits:

def f(x: String)(implicit dispatcher: ExecutionContextExecutor, mat: ActorMaterializer) = ???

And I want to create a helper method like:

def g1(y: String) = f("uri1" + y)
def g2(y: String) = f("uri2" + y)

Of course, this cannot compile as no implicits found for parameter ex: ExecutionContext for method g.

I don't want to repeat the implicits declaration in g.
So, what's the idiomatic solution for this case?

Wharton answered 4/12, 2019 at 11:29 Comment(1)
The idiomatic solution is to repeat the implicit declaration. If all the methods are part of the same class, you may move the implicit to the constructor of the class.Merras
N
5

Idiomatic solution is to repeat implicit parameters.

If you repeat the same set of implicit parameters many times then idiomatic solution is to introduce your type class (or just single implicit) instead of that set of implicits and use this type class.

Not idiomatic solution is to introduce macro annotation that will generate implicit parameter section for methods.

Sometimes you can transfer implicits to some level above

class MyClass(implicit val ec: ExecutionContext) extends ExecutionContextAware {
  def f(x: String) = ???
  def g(y: String) = f("xxx" + y)
}

trait ExecutionContextAware {
  implicit def ec: ExecutionContext
}

or

trait MyTrait extends ExecutionContextAware {
  def f(x: String) = ???
  def g(y: String) = f("xxx" + y)
}
  
object Impl extends ExecutionContextAware {
  implicit def ec: ExecutionContext = ExecutionContext.Implicits.global
}

trait ExecutionContextAware {
  implicit def ec: ExecutionContext
}

Could you please also give an example with typeclass?

Suppose you have multiple type classes

trait TC1[A] {
  def foo = ???
}
trait TC2[A] {
  def bar = ???
}

and you have to repeat them in methods

def f[A](implicit tc1: TC1[A], tc2: TC2[A]) = ???

1. Then you can introduce your type class

trait TC[A] {
  def foo
  def bar
}

express it via TC1, TC2, ...

object TC {
  implicit def mkTC[A](implicit tc1: TC1[A], tc2: TC2[A]): TC[A] = new TC[A] {
    def foo = tc1.foo
    def bar = tc2.bar
  }
}

and use it

def f[A](implicit tc: TC[A]) = ???

2. Alternative approach is

trait TC[A] {
  implicit def tc1: TC1[A]
  implicit def tc2: TC2[A]
}

object TC {
  implicit def mkTC[A](implicit _tc1: TC1[A], _tc2: TC2[A]): TC[A] = new TC[A] {
    implicit def tc1: TC1[A] = _tc1
    implicit def tc2: TC2[A] = _tc2
  }
}

def f[A](implicit tc: TC[A]) = {
  import tc._
  ???
}

In your example with ExecutionContextExecutor, ActorMaterializer (for example following the 2nd approach) you can introduce

trait MyImplicit {
  implicit def dispatcher: ExecutionContextExecutor
  implicit def mat: ActorMaterializer
}

and replace

def f(x: String)(implicit dispatcher: ExecutionContextExecutor, mat: ActorMaterializer) = ???

with

def f(x: String)(implicit mi: MyImplicit) = {
  import mi._
  ???
}
Natoshanatron answered 4/12, 2019 at 11:39 Comment(3)
Could you please also give an example with typeclass? I'm still new to it.Wharton
@Wharton the typeclass solution won't work in your case. Since it is intended for combining multiple implicits in one, but you just have one. But is a good pattern to learnMerras
I updated the question with multiple implicits. @LuisMiguelMejíaSuárezWharton

© 2022 - 2024 — McMap. All rights reserved.