Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
599 views
in Technique[技术] by (71.8m points)

scala - Pass implicit parameter through multiple objects

I wonder is it possible to pass implicit params through singletons like that

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.greetBar doesn't affected by implicit value in main, but I want it to be affected without passing implicit param to greetBar, so is there any way to do something like that? Maybe there is a way to set an implicit for object but in outer of it?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

You should add implicit parameter to the method

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

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

or make the object a class and add implicit parameter to the class

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

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

I commented out default value /*= Greet("Hello")*/. If you want greetBar not to compile when there is no implicit in scope then you should keep it commented out. If you want behavior similar to greet (i.e. Greet("Hello") when there is no implicit in scope) then you should uncomment it.

Please notice that you can avoid repeating default value if you define lower-priority implicit in companion object

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

See also How to wrap a method having implicits with another method in Scala?

I want to do this to set Greet implict for all methods in Bar

In principle, you can do this with a macro annotation (but you shouldn't)

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
//}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

2.1m questions

2.1m answers

60 comments

57.0k users

...