最近看过的电影(9)

偶然在电视里看的《红灯》(Red Lights)是一部蛮吸引我的电影,尽管豆瓣上的评分不算高。

它的结尾完全出人意料,不过仔细想想前边也是留了很多伏笔的。主角是希里安.墨菲,这个演员我是在看《蝙蝠侠前传》和《风吹麦浪》的时候留下深刻印象的。片子很有意思,汤姆跟随玛格丽特教授揭露一些所谓有超能力的神棍们的骗局,结果却是主人公汤姆才是真正具有超能力的人,只是因为他因为对母亲的愧疚,一直不敢直面自己所具备的超能力,否定自己。

周末看的另一部《拳外重生》(Max Schmeling)讲述了德国的一个伟大的拳击手马克斯·施梅林的故事,也还不错。

scala雾中风景(18): postfix operator的问题

这个谜题也是邮件列表里看到的,当使用下面的代码时,编译正常:

object ListTests1 extends App {
    val numbers: List[Int] = (1 to 10).toList
    println(s"numbers: $numbers") 
}

但当把toList方法前边的点给去掉时,编译居然报错:

scala> :pas
// Entering paste mode (ctrl-D to finish)

object ListTests1 extends App {
    val numbers: List[Int] = (1 to 10) toList
    println(s"numbers: $numbers")
}

// Exiting paste mode, now interpreting.

<console>:9: error: type mismatch;
 found   : Unit
 required: Int
     println(s"numbers: $numbers")
            ^

可能第一眼以为是(1 to 10) toList这种写法有问题,但直接在repl下运行这一句的话,是ok的,只是警告toList是一个后缀操作符

scala> val numbers: List[Int] = (1 to 10) toList

<console>:7: warning: postfix operator toList should be enabled
by making the implicit value scala.language.postfixOps visible.
This can be achieved by adding the import clause 'import scala.language.postfixOps'
or by setting the compiler option -language:postfixOps.
See the Scala docs for value scala.language.postfixOps for a discussion
why the feature should be explicitly enabled.
   val numbers: List[Int] = (1 to 10) toList
                                      ^
numbers: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

这里有趣的是,这行表达式跟下一行表达式的结果也可以组成一个表达式;在使用后缀操作符的情况下,方法后边的表达式,优先解析为参数对待。这里本来 toList 方法是一个无参方法,但它的结果类型List是可以apply的,所以toList方法后边也可以接受Int类型的参数;而下一行的println得到是一个Unit类型,所以错误。

类似这个例子:

object Test {
    def say  =  println("say nothing")
    def say(s:String) = println("say " + s)
}

定义了2个say方法,一个无参和一个String类型参数的,当调用

Test.say
println("hello")

上面语句被解释为2个表达式,第一句调用的是无参的 say 方法,而写为:

Test say
println("hello")

这种情况下被解析为Test.say(println("hello")) 把下一行表达式的结果当作say的参数,因为类型不匹配而失败。如果在第一行加一个分号行为就不一样了:

Test say;
println("hello")

postfix operator对于设计DSL很有用,但平常程序里最好还是不要用。

相关内容:scala2.10中采纳了SIP-18:模块化语言特性

scala的诊断方法(5) 用scalac-aspects诊断scalac各阶段耗时

在scala-user邮件列表里看到的,有人说他有两个类用scalac编译非常慢,别人给出了一个工具可以诊断scalac的编译过程在各环节耗时情况,这个工具是基于aspectj的,尝试了一把。

先下载aspectj,然后用java -jar aspectj-1.8.2.jar安装,它是个图形界面的安装程序,过程就是文件解压到指定路径。

然后设置一下环节变量:

$ export ASPECTJ_HOME=/data/tools/aspectj/1.8
$ export PATH=${ASPECTJ_HOME}/bin:${PATH}

从github上clone一份scalac-aspects,然后运行一下样例:

$ ./scalac-aspects PerUnitTiming.aj Foo.scala
...

Per-file timings (all times are in micro seconds)
Foo.scala 1260255
    parser                    170939
    namer                     144580
    packageobjects            60
    typer                     223685
    patmat                    10843
    superaccessors            7899
    extmethods                4354
    pickler                   35800
    refchecks                 78355
    selectiveanf              2013
    selectivecps              1724
    uncurry                   26039
    tailcalls                 16333
    specialize                59948
    explicitouter             86753
    erasure                   236839
    posterasure               855
    lazyvals                  11423
    lambdalift                38314
    constructors              43564
    flatten                   466
    mixin                     20772
    cleanup                   10301
    icode                     27698
    inliner                   626
    inlineExceptionHandlers   28
    closelim                  26
    dce                       18

对这个简单的只定义了一个方法的Foo.scala,编译过程最耗时也是在typererasure 阶段,估计大部分代码的编译过程类型相关的处理都会占大头。

maven调试web应用

mvn tomcat7:run运行web应用的一个小脚本,方便debug:

$ cat mvn-tomcat
#!/bin/bash

suspend="n"
if [ "$1" != "" ]; then
  lower=`echo $1 | tr '[:upper:]' '[:lower:]'`
  if [ "$lower" == "y" ] || [ "$lower" == "n" ]; then
    suspend=$lower
  else
    echo "param error" && exit -1;
  fi
fi

port=8080
if [ "$2" != "" ]; then
  re='^[0-9]+$'
  if ! [[ "$2" =~ $re ]] ; then
     echo "port: error, not a number" && exit -2;
  else
    port=$2
  fi
fi

export MAVEN_OPTS=-agentlib:jdwp=transport=dt_socket,address=8000,server=y,suspend="$suspend"
mvn tomcat7:run -Dmaven.tomcat.port="$port"

把脚本放到放到PATH路径下(比如~/bin目录下)。使用方式:

$ mvn-tomcat # 默认8080端口

$ mvn-tomcat y # 断点suspend

$ mvn-tomcat n 7001 # 指定tomcat用7001端口