这个问题是在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
时,指定它的类型是Route
即 Request => 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
类型在赋值时,理解成一个带有副作用的”过程”,这个过程接受无论什么类型的表达式,执行这些表达式,但最终返回的是()
这个值。
长知识了,感谢
Pingback引用通告: scala雾中风景(21): auto-tupling与auto-detupling | 在路上