Wednesday, June 24, 2015

Scala筆記: 回顧Option以及認識新朋友Either

最近又開始比較仔細的研究playframework,
在追Action的時候不小心認識了Either,(這樣寫起來怎麼有點花心感XD)
就趁機整理一下啦!

Either顧名思義就是不是這個就是那個(繞口令來著)
這樣是不是會讓你想起不是有(Some)就是沒有(None)的Option呢?
以下是相關的class以及他們的繼承關係.
sealed abstract class Option[+A] extends Product with Serializable
final case class Some[+A](x: A) extends Option[A]
case object None extends Option[Nothing]
abstract final class Nothing extends Any

之前在處理error handling的時候有用過Option,
成功回傳Some,失敗就回傳None,
但其實會有些問題, 因為我們有時候會需要針對不同的失敗原因個別處理,
像是我們要從資料庫讀取某筆資料, 可能會因為網路問題, 或是資料庫過於忙碌等等因素造成失敗
或者是我們成功得到資料庫的回應但是該筆資料並不存在
把這些林林總總的狀況概括用None來表示其實會太簡略

這個情況下我們就可以使用Either,
以下是相關的class以及他們的繼承關係.
sealed abstract class Either[+A, +B] extends AnyRef
final case class Left[+A, +B](a: A) extends Either[A, B] with Product with Serializable
final case class Right[+A, +B](b: B) extends Either[A, B] with Product with Serializable
他的值可以是類型A或類型B,
通常回傳Left是用來表示失敗,用Right表示成功

因為Action的部分有點複雜, 這邊就從playframework的OAuth library貼個簡單的範例.
  def retrieveRequestToken(callbackURL: String): Either[OAuthException, RequestToken] = {
    val consumer = new DefaultOAuthConsumer(info.key.key, info.key.secret)
    try {
      provider.retrieveRequestToken(consumer, callbackURL)
      Right(RequestToken(consumer.getToken(), consumer.getTokenSecret()))
    } catch {
      case e: OAuthException => Left(e)
    }
  }
再加上Left跟Right都是case class,
所以我們抓到結果之後可以簡單地用match處理回傳值
val reqTok = retrieveRequestToken("localhost:9000/callback")
reqTok match {
  case Right(tok) => // success, continue 
  case Left(ex) => // fail to get request token, dothing
}
大概是這樣!

另外再補充一些細節,
1. sealed
被加上sealed關鍵字的類別, 只能被定義在同一個原始碼檔案的類別繼承.
2. Option[+A]
是covariance的概念, 指的是D如果是A的子類別, 則Option[D]也是Option[A]的子類別


ref:
- Either
- Left
- Option
- 探索Playframework: 如何正確使用thread pool