Scala的“if ... else”可以作为库函数实现吗?

时间:2011-05-26 08:01:04

标签: scala if-statement keyword control-flow

我想知道if … else是否可以通过特殊的编译器处理在Predef中实现,与classOf[A]的方式类似:定义在{{1}实现由编译器填充。

当然,许多人会惊讶地发现,Predef始终是if,而if始终是else,无论上下文如何。但是,将else定义为结果类型else的方法会将其从关键字列表中删除,并允许库设计者定义自己的if方法。 (我知道我可以使用任何关键字作为带反引号的标识符,但else之类的内容在代码中看起来很糟糕。)此类方法在{{{}等情况下讨论的情况下非常有用3}},在定义实际应该命名为`else`的方法时,人们被迫使用otherwise。 (也在SO this one, discussed on the mailing listhere上进行了讨论。)

所以:

  • 即使在理论上,这种方法是否可行,或者它是否会破坏Scala中的一些基本原则?
  • 缺点是什么?

4 个答案:

答案 0 :(得分:10)

也许我不理解您的问题,但您已经可以将if ... else ...实现为库函数。考虑一下:

class If[A](condition: =>Boolean)(ifBlock: =>A) {
  def els(elseBlock: =>A):A = condition match {
    case true => ifBlock
    case false => elseBlock
  }
}

new If(2==3)(
  println("equal")
) els (
  println("not equal")
)

当然,这并不是完全 if ... else ...所做的事情,但我认为会有一些改进。我曾经为一种内置模式匹配的语言实现了一个非常简单的解释器,if ... else ...的实现与我在这里实现的方式非常相似。

答案 1 :(得分:8)

简短的回答是“是”;某些谓词上的分支逻辑可以实现为库函数。

值得指出的是,正如Viktor Klang和其他人所指出的那样,if / else实质上是折叠一个布尔值。折叠是我们经常做的事情 - 有时它是清晰明确的,有时不是。

// Either#fold is explicit
scala> Left[String, Double]("fail") fold(identity, _ + 1 toString)
res0: java.lang.String = fail

scala> Right[String, Double](4) fold(identity, _ + 1 toString)
res1: java.lang.String = 5.0

折叠选项不能明确地完成,但我们会一直这样做。

// Option has no fold - wont compile!
Some(5) fold(1+, 0)

// .. but the following is equivalent and valid
scala> Some(5) map(1+) getOrElse(0)
res3: Int = 6

布尔值上的分支逻辑也是一个折叠,你可以相应地pimp布尔值。注意使用by-name参数来实现延迟评估。如果没有此功能,则无法实现此类实现。

// pimped Boolean - evaluates t when true, f when false
class FoldableBoolean(b: Boolean) {
  def fold[A](t: => A, f: => A) =
    if(b) t else f
}

implicit def b2fb(b: Boolean) = new FoldableBoolean(b)

现在我们可以弃用布尔人了:

scala> true fold("true!", "false")
res24: java.lang.String = true!

scala> false fold("true!", "false")
res25: java.lang.String = false

答案 2 :(得分:3)

不只是if-else,而且任何语言功能都可以在称为“Scala Virtualized”的语言分支中重写

https://github.com/TiarkRompf/scala-virtualized

这构成了斯坦福大学PPL的Delite项目的基础,也是由Scala的欧盟资助资助的研究的核心。因此,您可以合理地期望它在未来的某个时刻成为核心语言的一部分。

答案 3 :(得分:3)

任何面向对象的语言(或带有运行时多态性的任何语言)都可以将条件实现为库特性,因为方法调度已经是一种更通用的条件形式。例如,Smalltalk绝对没有任何条件,除了方法调度。

除了语法方便之外,不需要任何类型的编译器魔法。

在Scala中,它看起来可能有点像这样:

trait MyBooleanLike {
  def iff[T <: AnyRef](thenn: => T): T
  def iffElse[T](thenn: => T)(els: => T): T
  def &&(other: => MyBoolean): MyBoolean
  def ||(other: => MyBoolean): MyBoolean
  def nott: MyBoolean
}

trait MyTruthiness extends MyBooleanLike {
  def iff[T](thenn: => T) = thenn
  def iffElse[T](thenn: => T)(els: => T) = thenn
  def &&(other: => MyBoolean) = other
  def ||(other: => MyBoolean) = MyTrue
  def nott = MyFalse
}

trait MyFalsiness extends MyBooleanLike {
  def iff[T](thenn: => T): T = null.asInstanceOf[T]
  def iffElse[T](thenn: => T)(els: => T) = els
  def &&(other: => MyBoolean) = MyFalse
  def ||(other: => MyBoolean) = other
  def nott = MyTrue
}

abstract class MyBoolean extends MyBooleanLike

class MyTrueClass extends MyBoolean with MyTruthiness {}
class MyFalseClass extends MyBoolean with MyFalsiness {}

object MyTrue extends MyTrueClass {}
object MyFalse extends MyFalseClass {}

只需添加一点隐式转换:

object MyBoolExtension {
  implicit def boolean2MyBoolean(b: => Boolean) =
    if (b) { MyTrue } else { MyFalse }
}

import MyBoolExtension._

现在我们可以使用它了:

object Main extends App {
  (2 < 3) iff { println("2 is less than 3") }
}

[注意:我的类型是相当弱的。我不得不作弊一点,以便在合理的时间内编译。对Scala类型系统有更好了解的人可能想要修复它。此外,现在我看一下,8个类,特征和对象,其中两个是抽象的,似乎有点过度设计;-)]

当然,模式匹配也是如此。任何具有模式匹配的语言都不需要其他类型的条件,因为无论如何模式匹配都更为通用。

[BTW:这基本上是this Ruby code的一个端口我几年前写的很有趣。]