8

例外を発生させる関数がいくつかあるとします。ラッピングしてお返ししていEither[Throwable, <function return type>]ます。Either(ではなく が必要だと仮定しましょうTry)。

def fooWrapper(arg1: FooArg1, arg2: FooArg2) =
  try Right(foo(arg1, arg2)) catch { case NonFatal(e) => Left(e) }

def barWrapper(arg1: BarArg1, arg2: BarArg2, a3: BarArg3) =
  try Right(bar(arg1, arg2, artg3)) catch { case NonFatal(e) => Left(e) }

...

ここで、bolierpllate コードを取り除く汎用ラッパーを書きたいと思います。あなたは何を提案しますか?

4

5 に答える 5

9

アリティに関して一般的なものを作成したいときはいつでも、Shapelessは必要なものを持っている可能性が高いです。この場合、次のように記述できます。

import scala.util.control.NonFatal
import shapeless._, ops.function._

def safeify[F, A <: HList, R, O](f: F)(implicit
  ftp: FnToProduct.Aux[F, A => R],
  ffp: FnFromProduct[A => Either[Throwable, R]]
) = ffp((a: A) =>
  try Right(ftp(f)(a)) catch {
    case NonFatal(ex) => Left(ex)
  }
)

ここで、次のような安全でないメソッドがあるとします。

def bad(s: String, i: Int) = s.toInt / i

ラップできます:

scala> val better = safeify(bad _)
better: (String, Int) => Either[Throwable,Int] = <function2>

これで、例外について心配する必要がなくなりました。

scala> better("1", 0)
res0: Either[Throwable,Int] = Left(ArithmeticException: / by zero)

scala> better("a", 1)
res1: Either[Throwable,Int] = Left(NumberFormatException: For input string: "a")

これは、古いFunctionN.

于 2014-05-13T15:18:08.753 に答える
4

次のような形式のものを書きます。

 def wrap[Value](f: => Value): Either[Value, Exception] = try{
   Right(f).right
 }
 catch{
   case ex: Exception => Left(ex).right
 }

 def foo(arg1: FooArg1, arg2: FooArg2) = wrap{
   //anything I'd have written before in foo
 }

しかし、これは構成しません。Tryとても素敵です。

更新: 正しい射影だけを処理したい場合は、正しい射影を返すだけです。今、それは構成します。

于 2014-05-13T14:29:34.010 に答える
3

最初の関数定義を変更したくない場合:

任意の数の引数に対して機能する 1 つのラッパーが必要ですか、それとも、考えられる引数の数ごとに一般的なラッパーで問題ありませんか? 後者の場合、次のものを使用できます。

def eitherify[A, B, C](f: Function2[A, B, C])(a: A, b: B) = {
    try Right(f(a, b)) catch { case NonFatal(e) => Left(e) }
}
def eitherify[A, B, C, D](f: Function3[A, B, C, D])(a: A, b: B, c: C) = {
    try Right(f(a, b, c)) catch { case NonFatal(e) => Left(e) }
}

eitherify(foo)などを行うことができますeitherify(bar)

于 2014-05-13T14:34:44.117 に答える
3

まだ十分な選択肢がないことは明らかなので、もう 1 つの方法はScalaUtils の試行メソッドを使用することです。ScalaUtils は、依存関係として追加することを気にしない、非常に小さな集中ライブラリです。Attempt メソッド (org.scalautils パッケージ オブジェクトにあります) は、toEither を呼び出すことができる Or 型を提供します。最初は、依存関係を追加する必要がないように Try を使用してこれを行うことを提案するつもりでしたが、toEither メソッドがないようで、驚きました。Or を使用すると、次のようになります。

scala> import org.scalautils._
import org.scalautils._

scala> def foo(i: Int, s: String): String = { require(i >= 0); s * i }
foo: (i: Int, s: String)String

scala> def bar(b: Boolean, i: Int, s: String): Int = { require(i >= 0); if (b) s.length else i }
bar: (b: Boolean, i: Int, s: String)Int

scala> def foo2(i: Int, s: String) = attempt(foo(i, s)).toEither
foo2: (i: Int, s: String)Either[Throwable,String]

scala> def bar2(b: Boolean, i: Int, s: String) = attempt(bar(b, i, s)).toEither
bar2: (b: Boolean, i: Int, s: String)Either[Throwable,Int]

scala> foo2(2, "ho")
res10: Either[Throwable,String] = Right(hoho)

scala> foo2(-2, "ho")
res11: Either[Throwable,String] = Left(java.lang.IllegalArgumentException: requirement failed)

scala> bar2(true, 3, "ho")
res12: Either[Throwable,Int] = Right(2)

scala> bar2(false, 3, "ho")
res13: Either[Throwable,Int] = Right(3)

scala> bar2(false, -3, "ho")
res14: Either[Throwable,Int] = Left(java.lang.IllegalArgumentException: requirement failed)

申し訳ありませんが、最初はあなたが 1 つの方法を望んでいたことを見逃していました。noziarが提案したように、おそらくオーバーロードするだけです:

scala> :paste
// Entering paste mode (ctrl-D to finish)

def safely[A, B, C](f: (A, B) => C): (A, B) => Either[Throwable, C] = (a: A, b: B) => attempt(f(a, b)).toEither
def safely[A, B, C, D](f: (A, B, C) => D): (A, B, C) => Either[Throwable, D] = (a: A, b: B, c: C) => attempt(f(a, b, c)).toEither


// Exiting paste mode, now interpreting.

safely: [A, B, C](f: (A, B) => C)(A, B) => Either[Throwable,C] <and> [A, B, C, D](f: (A, B, C) => D)(A, B, C) => Either[Throwable,D]
safely: [A, B, C](f: (A, B) => C)(A, B) => Either[Throwable,C] <and> [A, B, C, D](f: (A, B, C) => D)(A, B, C) => Either[Throwable,D]

scala> val foo3 = safely { foo _ }
foo3: (Int, String) => Either[Throwable,String] = <function2>

scala> val bar3 = safely { bar _ }    
bar3: (Boolean, Int, String) => Either[Throwable,Int] = <function3>

scala> foo3(2, "ho")
res5: Either[Throwable,String] = Right(hoho)

scala> foo3(-2, "ho")
res6: Either[Throwable,String] = Left(java.lang.IllegalArgumentException: requirement failed)

scala> bar3(true, 3, "ho")
res7: Either[Throwable,Int] = Right(2)

scala> bar3(false, 3, "ho")
res8: Either[Throwable,Int] = Right(3)

scala> bar3(false, -3, "ho")
res9: Either[Throwable,Int] = Left(java.lang.IllegalArgumentException: requirement failed)

過負荷 (および形状のない) を回避したい場合は、他の代替手段はmagnet patternです。これで 1 つの安全な方法にたどり着くと思いますが、オーバーロードの方が簡単だと思います。

于 2014-05-13T16:16:48.010 に答える
2

Scalaz には、あなたが説明したように、あなたのニーズを満たすかもしれないEither右バイアスの call があります。\/オブジェクトで使用fromTryCatchして、\/言及されたボイラープレートを削除できます。右に偏っているため\/、右に偏った理解に使用できます。

サンプルの使用法は次のとおりです。

for {
  r1 <- \/.fromTryCatch(something1)
  r2 <- \/.fromTryCatch(something2)
} yield something3(r1, r2)

上記の結果のタイプは次のようになりますThrowable \/ A

于 2014-05-13T14:52:02.683 に答える