如何制作“结合(s:S,i:I):S与I”形式的方法?

时间:2012-09-15 17:18:05

标签: scala types

鉴于此代码:

  trait S {
    def s: String
  }

  trait I {
    def i: Int
  }

  case class CC_S(s: String) extends S
  case class CC_I(i: Int) extends I
  case class CC_S_I(s: String, i: Int) extends S with I

  def combine(s: S, i: I): S with I = CC_S_I(s.s, i.i)

此测试成功:

Assert.assertEquals(CC_S_I("s", 1), combine(CC_S("s"), CC_I(1)))

(How)我可以将combine方法泛化为具有类型参数的特征吗?即我想定义这样的东西:

  trait MyTrait[A,B] {
    def combine(a: A, b: B): A with B
  }

所以我可以像这样使用它:

  class MyClass[A <: CC_S, B <: CC_I] extends MyTrait[A,B] {
    override def combine(s: A, i: B) = CC_S_I(s.s, i.i) //Note: this line does not compile...
  }

  Assert.assertEquals(CC_S_I("s", 1), new MyClass().combine(CC_S("s"), CC_I(1)))

更新即可。这可能是一个更好的例子:

  class MyClass[A <: S, B <: I] extends MyTrait[A,B] {
    override def combine(s: A, i: B) = CC_S_I(s.s, i.i) //Note: this line does not compile...
  }

请参阅特拉维斯回答的评论,以便看到答案。

1 个答案:

答案 0 :(得分:2)

问题只是CC_S_I实际上不是CC_SCC_I的子类型(尽管它是SI的子类型)。以下内容适用:

class MyClass extends MyTrait[S, I] {
  def combine(s: S, i: I): S with I = CC_S_I(s.s, i.i)
}

作为旁注,Shapeless库是值得研究的,如果你做了很多这样的事情。它允许您非常一般地编写combine方法:

import shapeless._

def combine[A, B, C, AL <: HList, BL <: HList, CL <: HList](a: A, b: B)(implicit
  aIso: Iso[A, AL],
  bIso: Iso[B, BL],
  cIso: Iso[C, CL],
  p: PrependAux[AL, BL, CL]
): C = cIso from p(aIso to a, bIso to b)

现在你只需要一些样板:

implicit val ccsIso = Iso.hlist(CC_S.apply _, CC_S.unapply _)
implicit val cciIso = Iso.hlist(CC_I.apply _, CC_I.unapply _)
implicit val ccsiIso = Iso.hlist(CC_S_I.apply _, CC_S_I.unapply _)

你可以写:

scala> val x = combine[
     |   CC_S, CC_I, CC_S_I, String :: HNil, Int :: HNil, String :: Int :: HNil
     | ](CC_S("s"), CC_I(1))
x: CC_S_I = CC_S_I(s,1)

不幸的是,这种配方需要使用巨大的类型注释,但这应该可以让您了解什么是可能的,并且您可以采取一些措施来使用清洁。