web-dev-qa-db-ja.com

Scalaでオプションをどちらかに変換

ScalaでOption[Int]Either[String, Int]に変換する必要があるとします。私はこのようにしたい:

def foo(ox: Option[Int]): Either[String, Int] =
  ox.fold(Left("No number")) {x => Right(x)}

残念ながら、上記のコードはコンパイルされず、タイプEither[String, Int]を明示的に追加する必要があります。

ox.fold(Left("No number"): Either[String, Int]) { x => Right(x) }

タイプを追加せずにこの方法でOptionEitherに変換することは可能ですか?
OptionEitherに変換することをどのように提案しますか?

23
Michael

いいえ、この方法で行う場合、タイプを除外することはできません。

Left("No number")の型は_Either[String, Nothing]_であると推測されます。ちょうどLeft("No number")から、コンパイラはEitherの2番目の型をIntにしたいことを知ることができず、型推論はコンパイラがそれ程遠くまで行かないメソッド全体を見て、_Either[String, Int]_であると決定します。

これはさまざまな方法で行うことができます。パターンマッチングの例:

_def foo(ox: Option[Int]): Either[String, Int] = ox match {
  case Some(x) => Right(x)
  case None    => Left("No number")
}
_

または、if式を使用:

_def foo(ox: Option[Int]): Either[String, Int] =
  if (ox.isDefined) Right(ox.get) else Left("No number")
_

または_Either.cond_を使用:

_def foo(ox: Option[Int]): Either[String, Int] =
  Either.cond(ox.isDefined, ox.get, "No number")
_
39
Jesper

どのバージョンのScala=を使用していたのかわかりません。現在、Scala 2.12.6では、このようなコードでコンパイルの問題はありません。

_def foo(ox: Option[Int]): Either[String, Int] =
  ox.toRight("No number")
_

私がもう1つ言いたいのは、折り畳み(折り畳みメソッドを持っているものはほとんど折りたたむのが私の好みの方法ですが)はかなり頻繁に型パラメータの助けを必要とすることです。コンパイラーが式を型チェックできる方法は2つあります。型パラメーターを推測する方法と、明示的に定義されたパラメーターを見つける方法です。

あなたの例では、次のようにオプションを折りたたむ場合:

_def foo(ox: Option[Int]): Either[String, Int] =
  ox.fold(Left("No number") : Either[String, Int])(x => Right(x))
_

最初の引数に関する型情報を明示的に提供し、それを使用してfoldの型パラメーターを推測できます。型推論メカニズムを支援しています。

一方、次のようにfoldの型パラメーターを単に明示的に指定することもできます。

_def foo(ox: Option[Int]): Either[String, Int] =
  ox.fold[Either[String, Int]](Left("No number"))(x => Right(x))
_

これで、実際の(値レベルの)引数には余分な型情報が散らばらず、コンパイラーがそれを調べたときに型推論が行われなくなり、明示的に提供されているfoldの型パラメーターがすぐにわかります。角括弧を使用して、typeパラメーターを明示的に指定します。

x => Right(x)に関するもう1つのポイントは、Right caseクラスのコンパニオンオブジェクトのapplyメソッドにxを渡す以外は何もしない新しい関数リテラルを実際に作成していることです。適切な形状の機能がすでに利用可能です。 xを取り、Right(x)を返します。 applyメソッドです。直接参照する(渡す)ことができます。

_def foo(ox: Option[Int]): Either[String, Int] =
  ox.fold[Either[String, Int]](Left("No number"))(Right.apply)
_
7
Peter Perháč