Scala构造函数抽象

时间:2011-11-25 14:31:10

标签: scala polymorphism

Scala中可以使用以下内容:

scala> val l = List
l: scala.collection.immutable.List.type = scala.collection.immutable.List$@7960c21a

scala> l ( 1, 2, 3 )
res0: List[Int] = List(1, 2, 3)

换句话说,Scala具有更高阶的多态性。我想使用高阶多态来执行以下操作。

sealed abstract class A { def eval () : A }
case class A0 () extends A { ... }
case class A1 ( a : A ) extends A { ... }
case class A2 ( a : A, b : A ) extends A { ... }
....

所以我有一堆case类,A的子类,其构造函数不一定采用相同数量的参数。我也希望有一个“通用”案例类,如下所示:

case class ApplyA ( c : ???, l : List [ A ] ) extends A {
   def eval () : A = { ??? } }

这个想法是ApplyA将第一个参数作为A子类型的构造函数和参数列表。然后eval方法使用构造函数构造一个适当的类(如果可能)(即列表具有正确的长度)并返回它 (这对应于上面l ( 1, 2, 3)示例中的List)。 ApplyA的第一个构造函数的参数类型是什么?

这应该可以使用更高阶的多态性,但我无法弄清楚如何。我知道即使不使用高阶多态,只需在函数中包装构造函数,然后将这些函数作为第一个参数传递给ApplyA的构造函数,我就可以做到这一点,但我想了解如何使用更高的函数 - 直接命令多态。

2 个答案:

答案 0 :(得分:12)

@alexey_r非常正确,您的List示例不涉及更高阶的多态性。但是如果你准备使用一些type-level heavy artillery,你可以抽象出A{0,1,2}构造函数的arity来获得看起来非常接近你所要求的东西。

要注意的第一点是,正如所写,你的'通用'类不可能实现,

case class ApplyA(c : ???, l : List[A]) ...

因为构造函数c的arity与列表l的长度之间没有编译时可检查的关系。我们可以通过将List替换为HList并帮助自己从具有任意arity的普通函数转换为具有单个HList参数的函数来解决该问题,

import shapeless.HList._
import shapeless.Functions._

sealed abstract class A { def eval() : A }
case class A0 () extends A { def eval() = this }
case class A1 ( a : A ) extends A  { def eval() = this }
case class A2 ( a : A, b : A ) extends A  { def eval() = this }

case class ApplyA[C, L <: HList, HF](c : C, l : L)
  (implicit hl : FnHListerAux[C, HF], ev : HF <:< (L => A)) extends A {
    def eval () : A = hl(c)(l)
  }

val a : A = A0()

val a0 = ApplyA(A0.apply _, HNil)
val a1 = ApplyA(A1.apply _, a :: HNil)
val a2 = ApplyA(A2.apply _, a :: a :: HNil)

隐式参数hl : FnHListerAux[C, HF]提供从构造函数(无论它是什么)到单个HList参数的函数的转换。隐式参数ev : HF <:< (L => A)证明提供的HList构造函数参数的长度具有正确的长度(并且类型为FWIW,但在此示例中几乎不相关)。

答案 1 :(得分:9)

问题是List示例根本不涉及任何高阶多态。 List.apply只需要可变数量的参数:

def apply(xs: A*)

高阶多态包含将类型构造函数作为类型参数的方法或类型,例如

def fmap[F[_], A](x: F[A]): F[B]

所以不,你不能使用高阶多态。