11

もちろん、すべてのタイプに共通の祖先があることは理解していますが、私が言いたいのは次のことです。

動的型付け言語では、戻り値の型を「混合」するのが一般的です。一般的なケースは、データベースからデータを取得しようとする関数で、オブジェクト (見つかったデータで初期化) または FALSE (データが見つからない場合) を返します。

そのようなアンチパターンを示す小さな擬似コード:

function getObjectFromDatabase(object_id) {
  if(result = db_fetch_object("SELECT * FROM objects WHERE id = %d", object_id) {
    return result
  } else {
    return FALSE
  }
}

オブジェクト ID のデータが見つかった場合、DB レコードがオブジェクトとして返されます。そうでない場合は、ブール値を取得します。もちろん、複数の可能な戻り値の型を処理するのは、クライアントである私です。

Scala でこれを達成する唯一の方法は、可能なすべての戻り値の型の共通の先祖を見つけ、それを署名の戻り値の型として宣言することですか?

// Like so:
def getObjectFromDatabase(objectId: Int): Any = {
   val result = dbFetchObject("SELECT * FROM objects WHERE id = %d", object_id) 
   if(result) {
     return result
   } else {
     return false
  }
}

または、複数の可能な戻り値の型に注釈を付けることは可能ですか?

(関数の戻り値の型が可能な限り明確であることを強制されることを望んでいるため、これが可能になることを望んでいないことに注意してください。言語があいまいな戻り値の型を禁止していることを知って安心します。それが私が尋ねているより多くの理由です。)

4

3 に答える 3

22

はい、使用しますEither:

def getObjectFromDatabase(objectId: Int): Either[Boolean, DbResult] = {
   val result = dbFetchObject("SELECT * FROM objects WHERE id = %d", object_id) 
   if (result) Right(result) else Left(false)

}

getObjectFromDatabase(id) match {
  case Right(result) => // do something with result
  case Left(bool) => // do something with bool
}

または、結果がない場合に特定の値が必要ない場合は、次を使用しますOption

def getObjectFromDatabase(objectId: Int): Option[DbResult] = {
   val result = dbFetchObject("SELECT * FROM objects WHERE id = %d", object_id) 
   if (result) Some(result) else None
}

getObjectFromDatabase(id) match {
  case Some(result) => // do something with result
  case None => // do something about no results
}

呼び出すことができる最も一般的なメソッドのリストと、それらがパターン マッチングにどのように変換されるかについては、 Tony Morris のOption Cheat Sheetを参照してください。Option

他の 2 つの代替手段は、Scala 2.10 で新しく追加さValidationれた scalaz および からのものです。Try

たとえばValidation、StackOverflow には非常に優れた回答がいくつかあります

このTryブログ投稿を参照してください: The Neophyte's Guide to Scala Part 6: Error Handling With Try . 同じ著者が と に良い投稿をしOptionていEitherます。

于 2013-01-19T02:03:24.897 に答える
21

探しているものは、タグ付き共用体、バリアントバリアント レコード識別共用体素結合、または合計型と呼ばれます。

積の型と組み合わせると、代数的データ型になります。

Scala は代数データ型を直接サポートしていませんが、継承によって簡単にモデル化できるため、その必要はありません。(ScalaにはクローズドADTsealedをサポートする修飾子があります。)

注: Scala 3 の時点で、Scalaenumsの形式で代数和型を直接サポートしています。以下はすべて Scala 2 にのみ適用されます。

あなたの例では、戻り値の型が または のいずれSomeTypeSomeOtherTypeであることがわかっている場合、次のようにモデル化できます。

sealed trait ReturnType

final case class SomeType extends ReturnType
final case class SomeOtherType extends ReturnType

def meth: ReturnType

戻り値の型が 2 つあるだけでわからない場合は、同様にモデル化できます。

sealed trait ReturnType[A, B]

final case class Type1[A, B](a: A) extends ReturnType[A, B]
final case class Type2[A, B](b: B) extends ReturnType[A, B]

def meth: ReturnType[A, B]

これは実際には an と呼ばれるよく知られたデータ型で(これは anまたは a のEitherいずれかを保持するため)、Scala の標準ライブラリに として存在します。ABscala.util.Either

Maybeしかし、あなたの特定のケースでは、 orと呼ばれるより具体的なタイプがありOption、値が存在するかどうかという考えをカプセル化しています。次のようになります。

sealed trait Maybe[T]

case object None extends Maybe[Nothing]
final case class Just[T](value: T) extends Maybe[T]

def meth: Maybe[T]

繰り返しますが、これは既に Scala によって として提供されていscala.Optionます。

Eitheroverの利点Optionは、値がないことを示すだけでなく、失敗した場合にも情報を返すことができることです。また、値がない理由も伝えることができます。(慣例により、 の左側Eitherはエラー、右側は「有用な」値です。)

の利点Optionは、それがモナドであることです。(注:Eitherモナドを左または右にバイアスすることで作成できます。)

于 2013-01-19T02:38:04.917 に答える
2

実行時に各呼び出しでどのタイプを照会しているかを知っている場合、署名は次のようになります。

def getObjectFromDatabase[T](object_id: Int): T = {

または、そこで if/else ロジックをシミュレートするには、ここで Option を使用することをお勧めします。

def getObjectFromDatabase[T](object_id: Int): Option[T] = {
  ...
  if(result) Some(result)
  else None
}

使用例:

val result = getObjectFromDatabase[String](123123).getOrElse(whatever_you_need)
于 2013-01-19T02:02:37.093 に答える