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

8
我想知道是否可以通过特殊的编译器处理在Predef中实现if...else,类似于classOf[A]所做的方式:定义在Predef中,由编译器填充实现。
当然,很多人会感到放心,知道if始终是if,else始终是else,无论上下文如何。但是,将else定义为if结果类型的方法将其从关键字列表中删除,并允许库设计者定义自己的else方法。(我知道我可以使用反引号将任何关键字用作标识符,但是像`else`这样的东西在代码中看起来很糟糕。)这样的方法可能在讨论的情况下很有用,例如邮件列表上讨论的情况,人们被迫在定义实际上应该命名为else的方法时使用otherwise。(也在SO上讨论这里这里。)
所以:
1.即使在理论上,是否可能采用这种方法,或者它是否违反了Scala的一些基本原则?
2.缺点是什么?
4个回答

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 ...的实现方式与我在这里所做的方式非常相似。


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

对布尔型变量进行分支逻辑也是一种折叠操作,您可以相应地优化 Boolean 类。注意使用传名参数以实现惰性求值。如果没有这个特性,这样的实现就不可能。

// 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.10开始具有fold方法。 - Rich Oliver

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类型系统更好的人可能需要修复它。此外,现在看起来,八个类、特质和对象,其中两个是抽象的,似乎有点过度设计;-) ]

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

[顺便说一下:这基本上是我几年前为了好玩而编写的this Ruby code的一个移植版本。]


非常感谢您提供这个非常好的概念验证!我意识到可以像这样完成。我的问题实际上是,是否可以将 if ... else 的当前形式作为库函数实现。我认为您和 Kim Stebel 已经证明了可以这样做。 - Jean-Philippe Pellet

3

不仅限于if-else,在被称为“Scala虚拟化”的语言分支中,任何语言特性都可以被覆盖。

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

这是斯坦福PPL的Delite项目的基础,也是由Scala的欧盟资助支持的研究的核心。因此,你可以合理地期望它在未来成为核心语言的一部分。

很高兴知道这个消息!这确实看起来像是下一个合乎逻辑的步骤...我期待着未来 :-) - Jean-Philippe Pellet

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接