scala类型系统:25) type lambda

上文,我们以函数的视角来看待类型系统中的高阶特性,与一阶函数,高阶函数对比:

这种kind表达方式与函数的lambda很相似。实际上scala也确实支持(受限版本的) type lambda,(注,从scala in depth这本书中看到,后续的例子也是引自这本书)

// 定义一个函数,带类型参数
scala> def foo[M[_]] (f : M[Int]) = f

scala> foo[List] ( List(1,2,3) ) 
res9: List[Int] = List(1, 2, 3)

scala> foo ( List(1,2,3) )  // 类型参数可以省略,编译器会推断 
res9: List[Int] = List(1, 2, 3)

现在,在不改变方法声明的情况下,如何让下面这句通过?

scala> foo( (x:Int) => println(x) ) // 如何让这句能编译通过?

foo函数在定义时,参数类型必须是泛型类型。而 (x:Int) => println(x)是一段匿名函数(lambda),它的类型是 Int=>Unit

而Scala中的函数,在实现上是通过FunctionN这个类来封装的;对于Int=>Unit这样的函数类型,背后其实是:

Function1[Int, Unit] 

这么看的话,Function1类型也是一个泛型类型,只不过它的类型参数有2个,而我们定义的foo函数的类型参数是只有一个类型参数的泛型,如果直接调用的话,很明显不符合类型约束。

所以这里引入了type lambda,我们可以通过type lambda 来把类型转换为符合约束的形式:

scala> foo[ ({type X[Y] = Function1[Y, Unit]})#X ] ( (x:Int)=>print(x) ) 
res5: Int => Unit = <function1>

现在我们来分析一下 ({type X[Y] = Function1[Y, Unit]})#X 这段类型表达式(type lambda)是什么意思?

首先,假定你了解scala里type的用法,这里定义了一个类型别名:X[Y]

type X[Y] = Function1[Y, Unit]

X[Y] 这个类型等价于 Function1[Y, Unit]。而把它用大括号括起来是什么意思?

{type X[Y] = Function1[Y, Unit]}

注意,这里的上下文是类型声明,在类型声明中,大括号用来表示结构类型(structural type),比如:

def using[ C <% { def close(): Unit } ](resource: C) {
     … 
}

上面using函数里类型声明时定义了一个结构类型{ def close(): Unit }表示一个任何存在close方法的类型。

所以{type X[Y] = Function1[Y, Unit]} 就表示了这样一个匿名类型:对任何Function1[Y, Unit]类型,定义一个了别名为X[Y]的类型。

然后通过(…)#X 的方式来获取这个匿名类型中的X类型,而这个X是符合foo函数的类型参数约束的。

scala类型系统:25) type lambda》上有2个想法

  1. 在这里type lambda和partially applied function很像,不过是类型构造器上的。

    M的kind是 * -> *,而Function1的kind是*->*->*,不符合类型约束,所以通过在结构类型里用类型别名声明一个新的kind为*->*的类型构造器。

    我觉得这里结构类型的使用是很巧妙,不知道除了实现类型构造器的partially applied 之外,还有没有其他的用法?说scala的type lambda是受限的,是因为它依赖于类型别名,而类型别名表达能力不足的关系吗?

    • 说它受限,是因为lambda演算能解决的问题,type lambda并不能完全解决,它在功能上只相当于lambda演算的子集。

发表评论

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