我喜欢这种方式,你可以在Scala中编写单行方法,例如List(1, 2, 3).foreach(..).map(..).
但是有一种情况,有时在编写Scala代码时会出现这种情况,事情变得有点难看.例:
def foo(a: A): Int = {
// do something with 'a' which results in an integer
// e.g. 'val result = a.calculateImportantThings
// clean up object 'a'
// e.g. 'a.cleanUp'
// Return the result of the previous calculation
return result
}
Run Code Online (Sandbox Code Playgroud)
在这种情况下,我们必须返回一个结果,但是在计算完成后不能直接返回它,因为我们必须在返回之前做一些清理.
我总是要写一个三班轮.是否有可能编写一个单行代码来执行此操作(不更改类A,因为这可能是一个无法更改的外部库)?
这里涉及明显的副作用(否则调用顺序calculateImportantThings并且cleanUp无关紧要),因此建议您重新考虑您的设计.
但是,如果这不是一个选项,你可以试试像,
scala> class A { def cleanUp {} ; def calculateImportantThings = 23 }
defined class A
scala> val a = new A
a: A = A@927eadd
scala> (a.calculateImportantThings, a.cleanUp)._1
res2: Int = 23
Run Code Online (Sandbox Code Playgroud)
元组值(a, b)等同于应用程序Tuple2(a, b),Scala规范保证其参数将从左到右进行计算,这就是您想要的.
这是一个完美的用例为try/ finally:
try a.calculateImportantThings finally a.cleanUp
Run Code Online (Sandbox Code Playgroud)
这是有效的,因为try/catch/finally是scala中的表达式,意味着它返回一个值,更好的是,无论计算是否抛出异常,都可以进行清理.
例:
scala> val x = try 42 finally println("complete")
complete
x: Int = 42
Run Code Online (Sandbox Code Playgroud)
也许你想使用红隼组合器?它的定义如下:
Kxy = x
因此,您可以使用要返回的值以及要执行的一些副作用操作来调用它.
您可以按如下方式实现它:
def kestrel[A](x: A)(f: A => Unit): A = { f(x); x }
Run Code Online (Sandbox Code Playgroud)
......并以这种方式使用它:
kestrel(result)(result => a.cleanUp)
Run Code Online (Sandbox Code Playgroud)
更多信息可以在这里找到:debasish gosh博客.
[更新]正如雅罗斯拉夫正确指出的那样,这不是红隼组合器的最佳应用.但是使用没有参数的函数定义类似的组合器应该没有问题,所以相反:
f: A => Unit
Run Code Online (Sandbox Code Playgroud)
有人可以使用:
f: () => Unit
Run Code Online (Sandbox Code Playgroud)
事实上,在这样的场合,有一个Haskell运算符:
(<*) :: Applicative f => f a -> f b -> f a
Run Code Online (Sandbox Code Playgroud)
例如:
ghci> getLine <* putStrLn "Thanks for the input!"
asdf
Thanks for the input!
"asdf"
Run Code Online (Sandbox Code Playgroud)
剩下的就是在scalaz中发现相同的运算符,因为scalaz通常会复制Haskell所拥有的所有内容.您可以包装值Identity,因为Scala不必IO对效果进行分类.结果看起来像这样:
import scalaz._
import Scalaz._
def foo(a: A): Int =
(a.calculateImportantThings.pure[Identity] <* a.cleanup.pure[Identity]).value
Run Code Online (Sandbox Code Playgroud)
然而,这是相当令人讨厌的,因为我们必须明确地将副作用计算包装在Identity中.事实上,scalaz做了一些隐式转换为Identity容器的魔法,所以你可以写:
def foo(a: A): Int = Identity(a.calculateImportantThings) <* a.cleanup()
Run Code Online (Sandbox Code Playgroud)
你也需要提示编译器不知何故最左边的是在身份单子.以上是我能想到的最短路.另一种可能性是使用Identity() *> foo <* bar,它将调用foo和bar按顺序的效果,然后产生值foo.
要返回ghci示例:
scala> import scalaz._; import Scalaz._
import scalaz._
import Scalaz._
scala> val x : String = Identity(readLine) <* println("Thanks for the input!")
<< input asdf and press enter >>
Thanks for the input!
x: String = asdf
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
498 次 |
| 最近记录: |