'implicit' 在 Scala 中没有像想象的那样工作

'implicit' in Scala not working as thought

为了理解 'implicit' 在 Scala 中的工作原理,我编写了以下代码,但它没有给我预期的结果。

我以为我理解 'implicit' 的用法(这显然不是真的)。我有三个 classes,Plant、Dog 和 myPet。 Dog 有一个函数 'sound' 其中 returns "woof"。 Plant 有一个函数 "tallOrShort" 其中 returns "tall"。这些函数描述了 Dog and Plant 的特征。

我想在 myPet 中编写一个通用代码,我可以将 Dog 或 Plant 的实例传递给它。我称这个函数为'describe'。 describe 函数应该为 Dog 实例打印 "woof",为 Plant 实例打印 "tall"。

狗和植物是独立的 classes(无子类型)。我想我可以在 Dog and Plant class 中使用 'implicit' 到 "add" 这个功能。我的理解是,通过使用隐式,可以将 Dog 或 Plant 隐式转换为 'something',然后可以分别为 Dog 和 Plant 调用 'sound' 或 'tallOrShort'。我写了下面的代码,但它不起作用。

我从 Dog class 开始

//create Dog
scala> class Dog {
| def sound = "woof"
| }

defined class Dog

//create trait (the contract interface which Dog can be member of
scala> trait myPetTrait[A] {
| def describePet(a:A):String
| }

defined trait myPetTrait

//implicit conversion should happen using this code?
scala> implicit object dogIsPet extends myPetTrait[Dog] {
| def describePet(d:Dog) = d.sound
| }

defined object dogIsPet

现在首先,我想按如下方式只定义一个通用函数 'describe',但我无法将 Dog 或 Plant 实例传递给它。

//'describe' function. It should work for both Dog and Plant
scala> def describe[A](implicit pt:myPetTrait[A]) = {println(pt.describePet(_:A))}
describe: [A](implicit pt: myPetTrait[A])Unit

scala> describe(new Dog)
<console>:21: error: type mismatch;
found   : Dog
required: myPetTrait[?] 
describe(new Dog)

问题 1 - 以上有什么问题? Dog 不应该转换为 myPetTrait[Dog] 吗?

然后我想创建一个class(myPet)并在class中定义'describe'。那也不行

//metPet It should work for both Dog and Plant       
scala> class myPet[A](a:A) {
| def describe[A](implicit pt:myPetTrait[A]) = {println(pt.describePet(_:A))}
| }
defined class myPet

scala> new myPet(new Dog).describe
<function1> 

问题 2 - 如果我将 describe 放在 myPet 中,为什么至少可以编译?为什么我得到的是函数文字 (function1),而不是我期望的打印结果

为了继续试验,我再次启动了 REPL,并在创建 describe 和 myPet 之前声明了 Plant

//new REPL session. created Dog  
scala> class Dog {
| def sound = "woof"
| }
defined class Dog

//created plant
scala> class Plant {
| def tallOrShort = "tall"
| }

defined class Plant
//created trait
scala> trait myPetTrait[A] {
| def describePet(a:A):String
| }

defined trait myPetTrait

//code which should I think help in implicit conversion
scala> implicit object plantIsPet extends myPetTrait[Plant] {
| def describePet(p:Plant)  = p.tallOrShort
| }
defined object plantIsPet

//describe still doesn't work
//describe shuold work for both Plant and Animal
scala> def describe[A](implicit pt:myPetTrait[A]) = {println(pt.describePet(_:A))}
describe: [A](implicit pt: myPetTrait[A])Unit

//not sure why this error comes
scala> describe(new Dog)
<console>:21: error: type mismatch;
found   : Dog
required: myPetTrait[?] 
describe(new Dog)

//get ambiguity error in this line
scala> new myPet(new Dog).describe     //myPet gives ambiguity error

我收到不明确的隐式值错误,指出类型 dogIsPet.type 的对象 dogIsPet 和类型 plantIsPet.type 的对象 plantIsPet 都匹配预期类型 myPetTrait[A]

问题 3 - 为什么 Scala 会抱怨歧义?这可能是因为 dogIsPet.type 和 plantisPet.type 是相同的 'type'。我如何使这段代码起作用?

我想你错过了这个(describe 必须采用 a 类型 Aimplicit 合同)

myPet

中像这样定义describe
class myPet[A](a:A) {
 def describe(implicit pt:myPetTrait[A]) = {
   println(pt.describePet(a))
 }
}
myPet之外的

describe可以这样声明

def describe[A](a: A)(implicit pt:myPetTrait[A]) = {println(pt.describePet(a))}

Scala 对此有特殊的语法

def describe[A: myPetTrait](a: A) = println(implicitly[myPetTrait[A]].describePet(a))

Scala REPL

scala> def describe[A](a: A)(implicit pt:myPetTrait[A]) = {println(pt.describePet(a))}
describe: [A](a: A)(implicit pt: myPetTrait[A])Unit

scala> describe[Plant](new Plant)
tall

或者使用scala语法糖

scala> def describe[A: myPetTrait](a: A) = println(implicitly[myPetTrait[A]].describePet(a))
describe: [A](a: A)(implicit evidence: myPetTrait[A])Unit

scala> describe[Plant](new Plant)
tall

scala> trait myPetTrait[A] { def describePet(a:A):String } 
defined trait myPetTrait

scala> class Dog { def sound = "woof" } 
defined class Dog

scala> implicit object dogIsPet extends myPetTrait[Dog] { def describePet(d:Dog) = d.sound }  
defined object dogIsPet

scala> def describe[A: myPetTrait](a: A) = println(implicitly[myPetTrait[A]].describePet(a)) 
defined function describe

scala> implicit object dogIsPet extends myPetTrait[Dog] { def describePet(d:Dog) = d.sound }  
defined object dogIsPet

scala> describe[Dog](new Dog) 
woof

scala> class myPet[A](a:A) {
      def describe(implicit pt:myPetTrait[A]) = {
        println(pt.describePet(a))
      }
    } 
defined class myPet

scala> new myPet[Dog](new Dog).describe 
woof

一处完整代码

Main.scala

object Implicits {
  trait myPetTrait[A] {
    def describePet(a:A):String
  }

  class Dog {
    def sound = "woof"
  }

  class Plant {
    def tallOrShort = "tall"
  }

  //evidence for Plant
  implicit object plantIsPet extends myPetTrait[Plant] {
    def describePet(p:Plant)  = p.tallOrShort
  }

  //evidence for Dog
  implicit object dogIsPet extends myPetTrait[Dog] {
    def describePet(d:Dog) = d.sound
  }

  def describe[A](a: A)(implicit pt:myPetTrait[A]) = {
    println(pt.describePet(a))
  }

  //syntactic sugar
  def describe2[A : myPetTrait](a: A) =
    println(implicitly[myPetTrait[A]].describePet(a))

  class myPet[A](a:A) {
    def describe(implicit pt:myPetTrait[A]) = {
      println(pt.describePet(a))
    }
  }

}


object Main {

  import Implicits._

  def main(args: Array[String]): Unit = {
    describe(new Dog)
    describe(new Plant)
    describe2(new Dog)
    describe2(new Plant)

    new myPet[Dog](new Dog).describe
    new myPet[Plant](new Plant).describe
  }
}

输出:

woof
tall
woof
tall
woof
tall