月度归档:2013年07月

scala2.11的repl下增加了kind命令

scala2.11的repl中新增了kind命令,见这里,对于kind的理解也可参考之前的文章

不过,下载了scala2.11-m3版本,发现kind命令并没有用 *号的方式来表达.

scala> :k Int
scala.Int's kind is A

scala> :k class Test{}
Test's kind is A

scala> :k List
scala.collection.immutable.List's kind is F[+A]

scala> :k class C[M[_]]
C's kind is X[F[A]]

scala> :k class C[M2[M1[_]]]
C's kind is Y[X[F[A]]]

对于自身类型,kind命令不采用*号表示,而是用 A 来表示。对于构造器类型 * -> * 采用 F[A]描述。

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类型系统:24) 理解 higher-kinded-type

首先我们从最基本的泛型来看:

现在我们对上面泛型中的类型参数再进一步,也是个泛型会如何呢?

可以看到,java中不支持类型参数也是泛型类型的情况,而scala支持。这是一个很重要的区别,scala在类型系统上要比java强大。我们现在简单对类型归纳一下,可以分为两类:

1)特定类型(proper type)
    比如 Int, String, List[Int], List2[List] 等类型

2) 泛型类型:用于构造特定类型(proper type)的类型
    比如 List, List2 等类型

现在我们来看 higher-kinded-type ,先要理解 kind 是什么意思,如果说类型(type)是对数据的抽象,比如1,2,3等抽象为Int类型,”hello”,”wolrd”等可抽象为String类型。那么kind则是对类型的抽象。

proper type 用 * 号表示:

泛型类型(用于构造proper type的类型)则可以用下面的方式表示,比如 Set[T], List[T],

Set 和 List 都是通过传递一个特定类型(proper type)然后构造出一个特定类型(proper type),用kind表示为:
* -> *

再如 Pair[K,V] 泛型类型

Pair 通过传递2个特定类型(proper type)然后构造出一个特定类型(proper type), 用kind表示为:
(*,*) -> *

如果泛型类型中的参数类型又是一个泛型,比如前边的 List2

List2 通过传递一个泛型类型(类型构造器),然后构造出一个特定类型,用kind表示为:
(*->*) -> *

这种类型参数也是泛型类型的类型,称之为高阶(higher)kind,是不是很像高阶函数?借用这张图做个汇总:

阿里技术嘉年华2013

这次阿里技术嘉年华上分享了《Scala中的函数式特征》与之前分享过的一篇ppt部分内容重合,不过也介绍了一下scala类型系统中的函数式特征。正是因为scala的类型系统支持高阶的概念,使得它也具备了部分解决问题的能力。type lambda 这个概念是从《Scala in depth》这本书里看到的。ppt可以从这里下载。

再说说这次嘉年华上的一些体会和收获。我是在周六上午10点的后端技术-编程语言专场的一个workshop上做的分享。开始担心关注scala的人很小众,并且又是上午最开始,来的人会很少,不过情况还好。CrazyJVM等scala粉丝都过来了,结束之后有两个从西安thoughtworks来的哥们儿跟我了解了一下阿里目前对scala使用的情况,以及不够普及的原因。

周六一天逛了几个主会场以及workshop,完整听了的有2个,一个是云OS的一个分享jvm指令及优化的,还有tinyfool讲述他创业经历的。有部分感受很认同,就是有时候你费劲力气做某事却总也无法做的很顺心,或达到预期的满意度;这可能就是天性,每个人都有自己擅长的领域也有不擅长的领域,尽可能符合自己的本性,会容易快乐一点。

还略微听了会儿一些开源项目的分享,tsar与nginx的配合,或许值得容器这边借鉴。还有天猫的一个对协程使用的分享,可惜没听到正题,有事先回家了。周日也有几场是我想听的,不过也有事没能去听。

scala编译器的一个bug

前天聚石找我问一个编译时期的问题,发现了一个编译器令人奇怪的行为,这个行为与java不一致,很怀疑是编译器的bug,但又担心是自己没有注意到某些细节或者scala里有意这样。把当时的问题场景简化后,大致是一个private修饰的成员在子类中存在同名的抽象成员,编译器会报错。

对比java:

% cat /tmp/A.java                 
public class A{
    public static class X1 {
        private void foo() {}
    }
    abstract public static class X2 extends X1 {
        abstract public void foo();   // it's ok
    }
}

子类X2继承自X1,在X1中定义的私有方法foo对X2是不可见的,所以X2中定义一个抽象的foo也毫不影响。

但在scala(2.10.2)里,这样的情况却编译报错:

% cat A.scala 
class A {
    private def v = 1
}

abstract class B extends A {
    def v: Int   // make it abstract , compile error! why ?
}

报错内容是认为这里override了一个更弱访问级别的方法:

% scalac A.scala    
A.scala:5: error: overriding method v in class B of type => Int; 
method v in class A of type => Int has weaker access privileges; it should not be private;
(Note that method v in class B of type => Int is abstract, 
and is therefore overridden by concrete method v in class A of type => Int)
abstract class B extends A {
               ^
one error found

实在难以理解,让人怀疑父类中的private成员在子类中的抽象成员是可感知的。在scala-user邮件列表里询问了一下,得到编译器的维护者Jason Zaugg的确认,说这是编译器的一个bug,他已经修复。https://github.com/retronym/scala/commit/c1da44c0421b5924d3276a4b30dff8ac7ef3e377

从他的记录里看到有说:Refinement types are still able to inherit private members from
all direct parents

嗯,这已经超出了我的了解范围。