scala类型系统:类型推导

类型推导是一门博大的学问,背后有繁冗的理论,好在非编译器开发者不用太多了解,我看到这方面的文章时立刻就知难而退了。这里蜻蜓点水的带过。

Scala采用的局部的(local)、基于流的(flow-based)类型推断;而非像ML,Haskell等语言采用的更加全局化的Hindley-Milner类型推断方式。

在《Programming in Scala》一书中提到基于流的类型推断有它的局限性,但是对于面向对象的分支类型处理比Hindley-Mlner更加优雅。

基于流的类型推导在偏应用函数场景下,不能对参数类型省略,正体现了它的局限性。下面的偏应用函数声明,在Hindley-Milner类型推导(基于全局的)可以正常的推导的,会在scala里报错:

scala> def foo(a:Int, b:String) = a+b

scala> val f = foo(200, _)
<console>:8: error: missing parameter type for expanded function ((x$1) => foo(200, x$1))
   val f = foo(200, _)
                    ^

上面第二个参数占位符缺乏类型。需要显式的声明变量f的类型

scala> val f: String=>String = foo(200, _)
f: String => String = <function1>

或者显式的声明占位符参数类型

scala> val f = foo(200, _:String)
f: String => String = <function1>

不过这里有一个细节是eta-expansion的情况下可以省略参数类型,比如:

scala> def foo(a:Int, b:String) = a+b

scala> val f = foo _
f: (Int, String) => String = <function2>

scala> val f = foo(_,_)
f: (Int, String) => String = <function2>

上面两种写法占位符都是对全部参数,foo _foo(_,_) 在编译过程会编译器会进行eta-expansioneta扩展会把这个表达式转换为与foo方法类型一致的函数对象。这是在规范里定义的。

而最开始的写法 foo(200, _)并不是eta-expansion(原因见之前的这篇)。这种情况下类型推导并不会从方法体的上下文去推断参数的类型。所以需要显式的声明参数类型。

另外,对于泛型方法的情况,类型推导也需要注意泛型参数,参考这篇

scala类型系统:类型推导》上有2个想法

  1. 在scala2.13中测试这种写法已经不再报错了
    “`
    def f2(a:Int, b:String) = b+a
    val f = f2(1, _)
    —–
    f2: f2[](val a: Int,val b: String) => String
    f: String => String = $Lambda$949/1509325680@3584b76e
    “`

发表评论

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