scala雾中风景(8): 高阶函数与Unit的谜题

这个问题是在spray-routing的使用过程中发现的,又是一个“障眼法”问题。

简化一下,假定有下面的类型:

scala> class Request

scala> type Route = Request => Unit

Route是一个函数类型,然后我们定义一个接受Route类型的方法

scala> def hf(r:Route) { println("ok") }
hf: (r: Request => Unit)Unit

现在传递一个Route类型的实例,调用hf方法:

scala>  hf( req => {} )
ok

上面传递了一个 req => {} 的函数对象,运行没有问题。

我们再定义一个Route的生成器:

scala> def routeGenerator: Route = req => println("do nothing")
routeGenerator: Request => Unit

把这个生成器作为参数传递给 hf 方法:

scala> hf(routeGenerator)
ok

跟刚才没什么区别。

现在我们如果传入一个看上去像是高阶函数的函数: req => routeGenerator,hf方法还会接受吗?

scala> hf ( req => routeGenerator )
ok

是不是有点奇怪?我传入的不是 Request => Route 类型吗?展开的话应该是 Request => (Request => Unit) 为什么hf方法也能接受呢?

这里核心的问题就在于 req => routeGenerator 这个函数实例,究竟是什么类型?这与编译器的类型推导逻辑有关。

当我们定义一个变量,不声明类型,把上面的函数对象赋给这个变量:

scala> val param =  (req:Request) => routeGenerator
param: Request => (Request => Unit) = <function1>

变量param的类型是 Request => (Request => Unit) 类型,与我们预测的一致。

如果定义param时,指定它的类型是RouteRequest => Unit,上面的函数对象还可以赋值吗?

scala> val param: Route  =  (req:Request) => routeGenerator
param: Request => Unit = <function1>

这里困惑我们的是,为什么函数对象右边的routeGenerator的类型在这次的上下文中变成了Unit,而不是我们定义的Route类型了呢?

如果你对Unit类型真的了解(参考之前的这篇:scala雾中风景(4): Unit类型),这个时候就不会被迷惑了。

因为Unit类型自身的特点,在赋值时,可以把任意类型的表达式赋值给它:

scala> val tmp:Unit = "hello"
tmp: Unit = ()

因为表达式背后会被翻译为: { "hello", () },同理,在之前的上下文里,定义了 param 是一个 Reuqest => Unit
类型的变量,在赋值时,编译器就会把 req => routeGenerator 翻译成:

req => { routeGenerator; () } 

这个问题看上去像是个高阶函数的问题,实际与高阶函数没关系。至于Unit类型为何在给变量赋值时设计成这样,可能与函数式语言的历史上已经是这样设计了,scala很可能是从ML那块继承的这个设计。

或许我们可以把Unit类型在赋值时,理解成一个带有副作用的”过程”,这个过程接受无论什么类型的表达式,执行这些表达式,但最终返回的是()这个值。

scala雾中风景(8): 高阶函数与Unit的谜题》上有2个想法

  1. Pingback引用通告: scala雾中风景(21): auto-tupling与auto-detupling | 在路上

发表评论

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