代码之家  ›  专栏  ›  技术社区  ›  Loic

从超类型解析隐式参数

  •  3
  • Loic  · 技术社区  · 8 年前

    如果为其超级类型a定义了隐式,是否可以解析类型B的隐式参数?

    下面是一个例子:

    我有一个可枚举的typeclass:

    trait Enumerable[A] {
    
      def name(a: A): String
    
      def list: List[A] 
    
      //... other methods
    }
    
    object Enumeration {
      def name[A, T >: A](a: A)(implicit ev: Enumerable[T]) = ev.name(a)
    
      def list[T](implicit ev: Enumerable[T]) = ev.list
    
      // ...
    }
    

    然后我定义了enumerable的一个实例:

    sealed trait Season
    
    case object Winter extends Season
    case object Spring extends Season
    case object Summer extends Season
    case object Fall extends Season
    
    implicit val seasonEnumerable = new Enumerable[Season] {
      override def list: List[Season] = List(Winter, Spring, Summer, Fall)
    }
    
    // working : 
    Enumeration.name(Winter: Season) shouldBe "winter"
    
    // faling : 
    Enumeration.name(Winter) shouldBe "winter"
    

    枚举。如果我不告诉scalac冬天是一个季节的话,名字(冬天)就失败了。我已经指定“name”方法签名中的隐式参数是a的超类型,但这还不够。。。

    有更好的方法吗?

    2 回复  |  直到 8 年前
        1
  •  3
  •   Alexey Romanov    8 年前

    Eduardo的回答解释了为什么 [A, T >: A] 这不起作用。但有一个比他给出的更简单的解决方案:不是引入 T

    def name[A](a: A)(implicit ev: Enumerable[T >: A] forSome { type T }) = ev.name(a)
    

    或者使用速记,

    def name[A](a: A)(implicit ev: Enumerable[_ >: A]) = ev.name(a)
    

    那么编译器只需要决定什么 T 是什么时候找的 ev .

        2
  •  3
  •   Eduardo Pareja Tobes    8 年前

    每当需要推断依赖类型的类型时,这是一个常见的不便。你的方法

    def name[A, T >: A](a: A)(implicit ev: Enumerable[T])
    

    被召唤时 Winter 第一 A 将推断 Winter.type 然后 T 成为 ,因为它符合该边界,并且在该点上没有更多约束。当然,编译器不会找到 Enumerable[Winter.type] .

    不过,对于类型成员有一个简单的解决方案:

    trait AnyEnumerable {
    
      type E
    
      def name[A <: E](a: A): String
      def list: List[E]
    }
    
    object Enumeration {
    
      def name[A](a: A)(implicit ev: AnyEnumerable { type E >: A }) = ev.name(a)
      def list[T](implicit ev: AnyEnumerable { type E = T }) = ev.list
      // ...
    }
    
    // an implicit for `Season`
    implicit val seasonEnumerable: AnyEnumerable { type E = Season } = 
      new AnyEnumerable {
    
        type E = Season
    
        def name[A <: Season](a: A): String = a.toString
        def list: List[Season] = List(Winter, Spring, Summer, Fall)
      }
    
    // compiles!
    val zzz = Enumeration.name(Winter)