try
方法是核心Ruby的常见扩展。例如,它在Rails中默认是可用的。try
只在对象不是nil
(Ruby的null
)时才在对象上执行方法或代码块。它的用法有三种:
-
在非
nil
对象上调用方法并返回结果:customer_or_nil.try(:save)
. -
如果结果不是
nil
,在表达式中链接任意代码块:obj.try { |non_nil_obj| do_something(non_nil_obj) }
。 -
(2)的扩展,不是用于可选的处理,而是作为继续链式表达式的一种方式,其中步骤n+1中的方法需要一个必须根据步骤n的结果计算的参数:
data.analyze.try { |result| result.compress(optimal_settings(result)) }.save
我对Scala等价的(3)或另一种Scala习语特别感兴趣,例如,这段与Apache Spark的DataFrame
相关的代码:
val df = ctx.sql("select * from my_table")
df.
repartition(max(1, df.rdd.partitions.size / 4)).
saveAsTable("repartitioned_table")
将被重构成如下内容(使用Ruby语法)
ctx.
sql("select * from my_table").
try { |df| df.repartition(max(1, df.rdd.partitions.size / 4)) }.
saveAsTable("repartitioned_table")
重构的目标是通过维护单个方法链来提高可读性,并通过将df
严格限定在链中绝对需要的步骤来减少作用域污染。
注意:我对讨论使用Option
进行可选处理的利弊特别不感兴趣,因为这不是这个问题所关注的try
的主要用例。
不在标准库中,但是等价于3很容易编写(try
是关键字,因此重命名为ap
(apply
的缩写)):
implicit class TryOp[A](x: A) {
def ap[B](f: A => B): B = f(x)
}
ctx.
sql("select * from my_table").
ap { df => df.repartition(max(1, df.rdd.partitions.size / 4)) }.
saveAsTable("repartitioned_table")