Passa il parametro implicito attraverso più oggetti
Mi chiedo se sia possibile passare parametri impliciti attraverso singleton come quello
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.greetBarnon è influenzato dal valore implicito in main, ma voglio che sia influenzato senza passare il parametro implicito a greetBar, quindi c'è un modo per fare qualcosa del genere? Forse c'è un modo per impostare un oggetto implicito ma al di fuori di esso?
Risposte
È necessario aggiungere un parametro implicito al metodo
object Bar {
def greetBar(implicit greet: Greet /*= Greet("Hello")*/) = Foo.greet("Bar")
}
implicit val greet: Greet = Greet("Goodbye")
Bar.greetBar // Goodbye Bar
oppure trasforma l'oggetto in una classe e aggiungi un parametro implicito alla classe
class Bar(implicit greet: Greet /*= Greet("Hello")*/) {
def greetBar = Foo.greet("Bar")
}
implicit val greet: Greet = Greet("Goodbye")
(new Bar).greetBar // Goodbye Bar
Ho commentato il valore predefinito /*= Greet("Hello")*/. Se non vuoi greetBarcompilare quando non c'è uno scopo implicito, dovresti tenerlo escluso. Se vuoi un comportamento simile a greet(cioè Greet("Hello")quando non c'è un ambito implicito) allora dovresti rimuoverlo.
Tieni presente che puoi evitare di ripetere il valore predefinito se definisci una priorità inferiore implicita nell'oggetto associato
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
Vedi anche Come racchiudere un metodo che ha impliciti con un altro metodo in Scala?
Voglio farlo per impostare
Greetimplict per tutti i metodi inBar
In linea di principio, puoi farlo con un'annotazione macro (ma non dovresti)
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 }"
}
}
}
Utilizzo:
@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
//}