scala - Clojure 的 'let' 在 Scala 中等效

标签 scala clojure scalaz let

我经常面临以下情况:假设我有这三个功能

def firstFn: Int = ...
def secondFn(b: Int): Long = ...
def thirdFn(x: Int, y: Long, z: Long): Long = ...

我还有calculate功能。我的第一种方法如下所示:

def calculate(a: Long) = thirdFn(firstFn, secondFn(firstFn), secondFn(firstFn) + a)

它看起来很漂亮,并且没有任何花括号 - 只有一个表达式。但这不是最佳的,所以我最终得到了这个代码:

def calculate(a: Long) = {
  val first = firstFn
  val second = secondFn(first)
  
  thirdFn(first, second, second + a)
}

现在是几个用大括号括起来的表达式。在这种时候我有点羡慕Clojure。与let function我可以在一个表达式中定义这个函数。

所以我的目标是定义 calculate具有一个表达式的函数。我想出了两个解决方案。

1 - 使用 scalaz 我可以这样定义它(有更好的方法使用 scalaz 来做到这一点吗?):

  def calculate(a: Long) = 
    firstFn |> {first => secondFn(first) |> {second => thirdFn(first, second, second + a)}}

我不喜欢这个解决方案的是它是嵌套的。更多val我的嵌套越深。

2 - 与 for理解我可以实现类似的目标:

  def calculate(a: Long) = 
    for (first <- Option(firstFn); second <- Option(secondFn(first))) yield thirdFn(first, second, second + a)

一方面,这个解决方案具有扁平结构,就像 let在 Clojure 中,但从另一方面来说,我需要将函数的结果包装在 Option 中并接收Option结果来自 calculate (我正在处理空值,这很好,但我不......也不想)。

有更好的方法来实现我的目标吗?处理这种情况的惯用方法是什么(也许我应该继续使用 val s...但是 let 这样做的方式看起来很优雅)?

从另一方面来说,它连接到 Referential transparency 。所有三个函数都是引用透明的(在我的示例中 firstFn 计算一些常数,如 Pi),因此理论上它们可以用计算结果替换。我知道这一点,但编译器不知道,所以它无法优化我的第一次尝试。这是我的第二个问题:

我可以以某种方式(可能通过注释)向编译器提示我的函数是引用透明的,以便它可以为我优化这个函数(例如,在那里放置某种缓存)?

编辑

感谢大家的精彩解答!不可能选出一个最佳答案(可能是因为它们都很好),所以我会接受得票最多的答案,我认为这很公平。

最佳答案

在非递归情况下,let 是 lambda 的重构。

def firstFn : Int = 42
def secondFn(b : Int) : Long = 42
def thirdFn(x : Int, y : Long, z : Long) : Long = x + y + z

def let[A, B](x : A)(f : A => B) : B = f(x)

def calculate(a: Long) = let(firstFn){first => let(secondFn(first)){second => thirdFn(first, second, second + a)}}

当然,这仍然是嵌套的。无法避免这一点。但你说你喜欢单子(monad)形式。这是身份单子(monad)

case class Identity[A](x : A) {
   def map[B](f : A => B) = Identity(f(x))
   def flatMap[B](f : A => Identity[B]) = f(x)
}

这是你的一元计算。通过调用 .x 解开结果

def calculateMonad(a : Long) = for {
   first <- Identity(firstFn)
   second <- Identity(secondFn(first))
} yield thirdFn(first, second, second + a)

但此时它看起来确实像原始的 val 版本。

Scalaz 中的 Identity monad 更加复杂

http://scalaz.googlecode.com/svn/continuous/latest/browse.sxr/scalaz/Identity.scala.html

关于scala - Clojure 的 'let' 在 Scala 中等效,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4881443/

相关文章:

java - 本地存储的数据库

Scala函数式编程体操

scala - 使用 ZIO 测试套件集成测试 HTTP 服务器

scala - IntelliJ : “Output exceeds cutoff limit” in scala worksheet

mysql - 由 : java. io.NotSerializableException : org. apache.spark.SparkContext 引起 - 在 Spark 中使用 JdbcRDD 时

multithreading - 如何并行运行 Scalaz 任务

scala - N-Tuple of Options to Option of N-Tuple

python - PySpark 中的 mkString 等价物是什么?

clojure - 客户端的环中间件?

clojure - Clojure 的 memoize 是否会强制对其参数进行评估?