Clojure在Scala中的'let'等价物

ten*_*shi 22 scala clojure let scalaz

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

def firstFn: Int = ...
def secondFn(b: Int): Long = ...
def thirdFn(x: Int, y: Long, z: Long): Long = ...
Run Code Online (Sandbox Code Playgroud)

我也有calculate功能.我的第一种方法可能如下所示:

def calculate(a: Long) = thirdFn(firstFn, secondFn(firstFn), secondFn(firstFn) + a)
Run Code Online (Sandbox Code Playgroud)

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

def calculate(a: Long) = {
  val first = firstFn
  val second = secondFn(first)

  thirdFn(first, second, second + a)
}
Run Code Online (Sandbox Code Playgroud)

现在有几个用大括号括起来的表达式.在这样的时刻,我羡慕Clojure一点点.使用let函数我可以在一个表达式中定义此函数.

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

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

  def calculate(a: Long) = 
    firstFn |> {first => secondFn(first) |> {second => thirdFn(first, second, second + a)}}
Run Code Online (Sandbox Code Playgroud)

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

2 - 通过for理解,我可以实现类似的东西:

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

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

有没有更好的方法来实现我的目标?处理这种情况的惯用方法是什么(可能我应该坚持下去val......但let这样做的方式看起来如此优雅)?

另一方面,它与参考透明度相关联.所有这三个函数都是参考透明的(在我的例子中firstFn计算一些像Pi一样的常量),所以理论上它们可以用计算结果代替.我知道这一点,但编译器没有,所以它无法优化我的第一次尝试.这是我的第二个问题:

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

编辑

谢谢大家的好消息!选择一个最佳答案是不可能的(可能是因为它们都很好)所以我会接受最多投票的答案,我认为这是公平的.

Jam*_*Iry 12

在非递归的情况下,让我们重组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)}}
Run Code Online (Sandbox Code Playgroud)

当然,那仍然是嵌套的.无法避免.但是你说你喜欢monadic形式.所以这是身份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)
}
Run Code Online (Sandbox Code Playgroud)

这是你的monadic计算.通过调用.x来打开结果

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

但在这一点上它确实看起来像原始的val版本.

Identity monad存在于Scalaz中,具有更高的复杂性

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


Cra*_*lin 7

坚持原始形式:

def calculate(a: Long) = {
  val first = firstFn
  val second = secondFn(first)

  thirdFn(first, second, second + a)
}
Run Code Online (Sandbox Code Playgroud)

它简洁明了,甚至对Java开发人员也是如此.它大致等同于let,只是不限制名称的范围.