shapeless(2): 对函数(值)实现参数化多态

上一篇,现在来谈谈shapeless怎么实现的函数(值)的多态。参考shapeless作者原文

先回顾一下scala里函数类型的定义(带有一个参数的):

trait Function1[-T, +R] {
    def apply(t : T) : R
}

我们只要稍作修改就可以支持对入参类型支持多态,即把apply方法声明为参数化的:

trait PolyFunction1[R] {
    def apply[T](t : T) : R
}

测试一下:

scala>  object test extends PolyFunction1[String] { def apply[T](p:T) = p.toString}

scala> test("hi")
res21: String = hi

scala> test(2)
res22: String = 2

现在还差一步,怎么让返回结果类型也支持多态?虽然我们也可以在函数结尾声明返回结果是T,但那只是表示入参类型与出参类型一致的函数,并不能表达所有的函数类型。

为了达到效果,我们必须对出参类型再进行抽象!也就是说我们用高阶类型来描述(higher-kinded type,可以参考这篇:scala类型系统:24) 理解 higher-kinded-type)

对于特定类型R,它们的高阶类型为C[_],比如:

List[Int] 对应的高阶类型,即类型构造器 List

Set[String] 对应的高阶类型,即类型构造器 Set

那对于 String, Int 这样非参数化的类型,它们的高阶类型(构造器)又是什么?我们可以假定存在一个这样的类型构造器:

scala> type Id[T] = T

Id这个类型构造器与任何类型参数结合,构造出来的结果类型与参数类型一致。

scala> val s:Id[String] = "hi"
s: Id[String] = hi

scala> val s:Id[Int] = 2
s: Id[Int] = 2

scala> def f(id:Id[_]) = print("ok")
f: (id: Id[_])Unit

scala> f(2)
ok

scala> f("hello")
ok

对于String,Int这样的类型,它们的高阶类型即类型构造器Id.(其实可以把这背后看做是一个类型级别的函数,非参数化的类型其构造器都可以看做是Id这样的一个构造器类型)

现在我们重新定义多态函数PolyFunction1,通过前边高阶类型的铺垫,我们现在把结果类型R与参数类型T之间看做是一种依赖关系,即R取决于T和构造器C[_] (关于依赖类型,参考 scala类型系统:28) 依赖类型):

构造器 C[_] 与参数 T 产生结果类型 C[T] 即 R == C[T]

这样我们在定义PolyFunction1时,只需要抽象出一个结果类型的类型构造器就可以了:

trait PolyFunction1[C[_]] {
    def apply[T](t : T) : C[T]
}

现在构造一个多态函数的实例,先用一个常见的类型构造器 Set做参数,结果类型是Set[T]

scala>  object test extends PolyFunction1[Set] { def apply[T](p:T) = Set(p)}
defined module test

scala> test("hi")
res23: scala.collection.immutable.Set[String] = Set(hi)

scala> test(2)
res24: scala.collection.immutable.Set[Int] = Set(2)

现在实现了我们上一篇期望的 singletonFn 函数能够满足 T => Set[T]的效果。

再测试使用Id这个类型构造器(高阶类型)做类型参数,结果类型是Id[T],也就是T

scala>  object test extends PolyFunction1[Id] { def apply[T](p:T) = p}

scala> test("hi")
res17: String = hi

scala> test(2)
res18: Int = 2

shapeless正是通过这种思路对函数实现的多态。

shapeless(2): 对函数(值)实现参数化多态》上有3个想法

  1. Pingback引用通告: 我所理解的monad(4):函子(functor)是什么 | 在路上

  2. scala> object test extends PolyFunction1[Id] { def apply[T](p:T) = p}

    scala> test(“hi”)
    res17: String = hi

    scala> test(2)
    res18: Int = 2

    最后一个例子不也是传入 是什么类型就返回什么类型吗,怎么就实现了返回结果的多态呢?没太看懂,请不吝赐教

    • 里面的 def apply[T](p:T) 方法是 override 父类里的 def apply[T](t : T) : C[T]

发表评论

电子邮件地址不会被公开。 必填项已用*标注