Passer un paramètre implicite à travers plusieurs objets

Oct 08 2020

Je me demande s'il est possible de passer des paramètres implicites à travers des singletons comme ça

case class Greet(g: String)

object Foo {
  def greet(name: String)(implicit greet: Greet = Greet("Hello")) = println(greet.g + " " + name)
}

object Bar {
  def greetBar = Foo.greet("Bar")
}


object Main {
  def main(args: Array[String]): Unit = {
    implicit val greet: Greet = Greet("Goodbye")
    
    Foo.greet("Sunshine") // Goodbye Sunshine
    Bar.greetBar // Hello Bar
  }
}

Bar.greetBarn'est pas affecté par la valeur implicite dans main, mais je veux qu'elle soit affectée sans passer de paramètre implicite à greetBar, alors y a-t-il un moyen de faire quelque chose comme ça? Peut-être existe-t-il un moyen de définir un objet for implicite mais à l'extérieur de celui-ci?

Réponses

3 DmytroMitin Oct 08 2020 at 17:59

Vous devez ajouter un paramètre implicite à la méthode

object Bar {
  def greetBar(implicit greet: Greet /*= Greet("Hello")*/) = Foo.greet("Bar")
}

implicit val greet: Greet = Greet("Goodbye")
Bar.greetBar // Goodbye Bar

ou faire de l'objet une classe et ajouter un paramètre implicite à la classe

class Bar(implicit greet: Greet /*= Greet("Hello")*/) {
  def greetBar = Foo.greet("Bar")
}

implicit val greet: Greet = Greet("Goodbye")
(new Bar).greetBar // Goodbye Bar

J'ai commenté la valeur par défaut /*= Greet("Hello")*/. Si vous greetBarne voulez pas compiler quand il n'y a pas de portée implicite, vous devez le garder en commentaire. Si vous voulez un comportement similaire à greet(c'est- à -dire Greet("Hello")lorsqu'il n'y a pas de portée implicite), vous devez le décommenter.

Veuillez noter que vous pouvez éviter de répéter la valeur par défaut si vous définissez implicitement une priorité inférieure dans l'objet compagnon

case class Greet(g: String)
object Greet {
  implicit val lowPriorityGreet: Greet = Greet("Hello")
}

object Foo {
  def greet(name: String)(implicit greet: Greet) = println(greet.g + " " + name)
}

object Bar {
  def greetBar(implicit greet: Greet) = Foo.greet("Bar")
}
// class Bar(implicit greet: Greet) {
//   def greetBar = Foo.greet("Bar")
// }

implicit val greet: Greet = Greet("Goodbye")

Foo.greet("Sunshine") // Goodbye Sunshine
Bar.greetBar // Goodbye Bar
// (new Bar).greetBar // Goodbye Bar

Voir aussi Comment encapsuler une méthode ayant des implicits avec une autre méthode dans Scala?

Je veux faire cela pour définir Greetimplicite pour toutes les méthodes dansBar

En principe, vous pouvez le faire avec une annotation de macro (mais vous ne devriez pas)

import scala.annotation.{StaticAnnotation, compileTimeOnly}
import scala.language.experimental.macros
import scala.reflect.macros.blackbox

@compileTimeOnly("enable macro annotations")
class greetAware extends StaticAnnotation {
  def macroTransform(annottees: Any*): Any = macro GreetAwareMacro.impl
}

object GreetAwareMacro {
  def impl(c: blackbox.Context)(annottees: c.Tree*): c.Tree = {
    import c.universe._

    val greet = TermName(c.freshName("greet"))
    val implicitGreet = q"""implicit val $greet: Greet = Greet("Hello")""" def isImplicit(param: Tree): Boolean = param match { case q"$mods val $_: $_ = $_" => mods.hasFlag(Flag.IMPLICIT) } annottees match { case q"$mods object $tname extends { ..$earlydefns } with ..$parents { $self => ..$body }" :: Nil => val body1 = body.map { case q"$mods def $tname[..$tparams](...$paramss): $tpt = $expr" => val paramss1 = if (paramss.nonEmpty && paramss.last.nonEmpty && isImplicit(paramss.last.head)) paramss.init :+ (paramss.last :+ implicitGreet) else paramss :+ List(implicitGreet) q"$mods def $tname[..$tparams](...$paramss1): $tpt = $expr" case notMethod => notMethod } q"$mods object $tname extends { ..$earlydefns } with ..$parents { $self => ..$body1 }"
    }
  }
}

Usage:

@greetAware
object Foo {
  def greet(name: String) = println(implicitly[Greet].g + " " + name)
}

@greetAware
object Bar {
  def greetBar = Foo.greet("Bar")
  def xxx(i: Int) = ???
  def yyy(i: Int)(implicit s: String) = ???
}

implicit val greet: Greet = Greet("Goodbye")

Foo.greet("Sunshine") // Goodbye Sunshine
Bar.greetBar // Goodbye Bar

//scalac: object Foo extends scala.AnyRef {
//  def <init>() = {
//    super.<init>();
//    ()
//  };
//  def greet(name: String)(implicit greet$macro$1: Greet = Greet("Hello")) = println(implicitly[Greet].g.$plus(" ").$plus(name)) //} //scalac: object Bar extends scala.AnyRef { // def <init>() = { // super.<init>(); // () // }; // def greetBar(implicit greet$macro$2: Greet = Greet("Hello")) = Foo.greet("Bar"); // def xxx(i: Int)(implicit greet$macro$2: Greet = Greet("Hello")) = $qmark$qmark$qmark;
//  def yyy(i: Int)(implicit s: String, greet$macro$2: Greet = Greet("Hello")) = $qmark$qmark$qmark
//}