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雾中风景(18): postfix operator的问题》上有5条评论

  1. hary

    learning-scalaz day-1中有段代码, 能分析下执行的原理么?

    trait CanTruthy[A] { self =>
    def truthys(a: A): Boolean
    }

    object CanTruthy {
    def apply[A](implicit ev: CanTruthy[A]): CanTruthy[A] = ev
    def truthys[A](f: A => Boolean): CanTruthy[A] = new CanTruthy[A] {
    def truthys(a:A): Boolean = f(a)
    }
    }

    //
    trait CanTruthyOps[A] {
    def self: A
    implicit def F: CanTruthy[A]
    final def truthy:Boolean = F.truthys(self)
    }

    //
    object ToCanIsTruthyOps {
    implicit def toCanIsTruthyOps[A](v: A)(implicit ev: CanTruthy[A]) =
    new CanTruthyOps[A] {
    def self = v
    implicit def F: CanTruthy[A] = ev
    }
    }

    implicit val intCanTruthy: CanTruthy[Int] = CanTruthy.truthys {
    case 0 => false
    case _ => true
    }

    10.truthy

    回复
  2. hary

    我这里还执行不了。
    :60: error: value truthy is not a member of Int
    10.truthy
    ^
    :44: warning: implicit conversion method toCanIsTruthyOps should be enabled
    by making the implicit value scala.language.implicitConversions visible.
    This can be achieved by adding the import clause ‘import scala.language.implicitConversions’
    or by setting the compiler option -language:implicitConversions.
    See the Scala docs for value scala.language.implicitConversions for a discussion
    why the feature should be explicitly enabled.
    implicit def toCanIsTruthyOps[A](v: A)(implicit ev: CanTruthy[A]) =

    回复
  3. hary

    漏了, 可以运行, 但还是值得分析:
    // 类型类!!!
    trait CanTruthy[A] { self =>
    def truthys(a: A): Boolean
    }

    // 工具方法
    object CanTruthy {

    def apply[A](implicit ev: CanTruthy[A]): CanTruthy[A] = ev

    def truthys[A](f: A => Boolean): CanTruthy[A] = new CanTruthy[A] {
    def truthys(a:A): Boolean = f(a)
    }
    }

    //
    // CanTruythy.xxxx 是不是会调用了apply而返回了一个ev呢?
    // 所以其实是ev.xxx? 不是的。 ?????

    //
    trait CanTruthyOps[A] {
    def self: A
    implicit def F: CanTruthy[A]
    final def truthy:Boolean = F.truthys(self)
    }

    //
    object ToCanIsTruthyOps {
    implicit def toCanIsTruthyOps[A](v: A)(implicit ev: CanTruthy[A]) =
    new CanTruthyOps[A] {
    def self = v
    implicit def F: CanTruthy[A] = ev
    }
    }

    import ToCanIsTruthyOps._

    // CanTruthy[Int]的一个隐式值
    implicit val intCanTruthy: CanTruthy[Int] = CanTruthy.truthys {
    case 0 => false
    case _ => true
    }

    10.truthy

    回复
  4. hary

    分析倒是能清楚, 我总觉得这里太绕了。 不知道作者是什么思路。

    回复
  5. hongjiang 文章作者

    你贴一堆代码也不说疑惑点是什么,怎么运行的(我连main方法都没看到),然后哪儿碰到了问题。你不能假设别人给你一样清楚你的上下文,所以很难有耐心去看一大坨代码猜测你想问什么。最好在 github gist 上写一个片段,这里代码格式不太好。

    回复

发表评论

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