IntList { def sum: Int = this match { case End => 0 case Pair(hd, tl) => hd + tl.sum } } final case object End extends IntList final case class Pair(head: Int, tail: IntList) extends IntList
型 A の Result は型 A の Success か String の文字列を伴う Failure です。 sealed trait Result[A] case class Success[A](result: A) extends Result[A] case class Failure[A](reason: String) extends Result[A] Success と Failure のどちらも、Result を拡張するところで渡される型引数 A を導入し ていることに気付きます。Success は型 A の値を持っていますが、Failure は型 A を導 入しているだけです。後節で変位を導入するときに、この実装について明快な方針を示 します。
{ case End => 0 case Pair(hd, tl) => 1 + tl.length } def double: IntList = this match { case End => End case Pair(hd, tl) => Pair(hd * 2, tl.double) } def product: Int = this match { case End => 1 case Pair(hd, tl) => hd * tl.product } def sum: Int = this match { case End => 0 case Pair(hd, tl) => hd + tl.sum } } final case object End extends IntList final case class Pair(head: Int, tail: IntList) extends IntList
にするだけです。 sealed trait LinkedList[A] { def fold[B](end: B, f: (A, B) => B): B = this match { case End() => end case Pair(hd, tl) => f(hd, tl.fold(end, f)) } } final case class Pair[A](head: A, tail: LinkedList[A]) extends LinkedList[A] final case class End[A]() extends LinkedList[A]
def fold[B](???): B = this match { case End() => ??? case Pair(hd, tl) => ??? } } final case class Pair[A](head: A, tail: LinkedList[A]) extends LinkedList[A] final case class End[A]() extends LinkedList[A] これは結果型として総称型引数を追加した構造的再帰のテンプレートです。
=> B で、単に型 B の値として最適化できます。 Pair は2引数を持ち、ひとつはリストの先頭で、もうひとつはリストの末尾です。head に ついての引数は型 A で、tail についての引数は再帰のなので型 B です。よって最終的 に型は (A, B) => B です。 def fold[B](end: B, pair: (A, B) => B): B = this match { case End() => end case Pair(hd, tl) => pair(hd, tl.fold(end, pair)) }
sum(x: Int, y: Int) = x + y } Sum.sum // <console>:9: error: missing arguments for method sum in object Sum; // follow this method with `_' if you want to treat it as a ... // Sum.sum // ^ (Sum.sum _) // res: (Int, Int) => Int = <function2> * 訳注:節番号が 5.3.2.2 になる内容です。おそらく誤植と考えられます。
B): B = this match { case End() => end case Pair(hd, tl) => pair(hd, tl.fold(end, pair)) } これによって下記のように呼び出すことができます。 fold(0){ (total, elt) => total + elt } 下記よりは読み易くなります。 fold(0, (total, elt) => total + elt)
def map[B](fn: A => B): LinkedList[B] = this match { case Pair(hd, tl) => ??? case End() => ??? } } final case class Pair[A](head: A, tail: LinkedList[A]) extends LinkedList[A] final case class End[A]() extends LinkedList[A]
tl) => { val newTail: LinkedList[B] = tl.map(fn) // Combine newTail and head to create LinkedList[B] } fn 関数を使用して先頭を B に変換し、末尾を再帰したリストから大きなリストを構築しま す。 case Pair(hd, tl) => Pair(fn(hd), tl.map(fn))
LinkedList[A] { def map[B](fn: A => B): LinkedList[B] = this match { case Pair(hd, tl) => Pair(fn(hd), tl.map(fn)) case End() => End[B]() } } 型とパターンが解決に導いてくれていることに気付きますよね。
F[B] を取得したいことにな ります。この処理を実行するメソッドを flatMap と呼びます。 Maybe について flatMap を実装してみましょう。まずは型を下書きするところから始め ます。 sealed trait Maybe[A] { def flatMap[B](fn: A => Maybe[B]): Maybe[B] = ??? } final case class Full[A](value: A) extends Maybe[A] final case class Empty[A]() extends Maybe[A]
A => Maybe[B]): Maybe[B] = this match { case Full(v) => fn(v) case Empty() => Empty[B]() } } final case class Full[A](value: A) extends Maybe[A] final case class Empty[A]() extends Maybe[A]
Full[A](value: A) extends Maybe[A] final case class Empty[A]() extends Maybe[A] 理想的には Empty の使用されていない型引数を下記のように取り除きたいです。 sealed trait Maybe[A] final case class Full[A](value: A) extends Maybe[A] final case object Empty extends Maybe[???]
OK である。 • A から B の派生型への関数は OK である。なぜなら、その結果型は B の属性す べてを持っているためだ。これは、結果型において共変である関数を示している。 • A の基底型から B への関数も OK である。なぜなら、Box が持つ A は関数が期 待する属性すべてを持っているためだ。 • A の派生型から B への関数は OK ではない。なぜなら、値は A の派生型とはおそ らく異なるためだ。
が総称でない場合、このように記述する。 sealed trait A[+T] final case class B[T](t: T) extends A[T] final case object C extends A[Nothing] このパターンはひとつ以上の型引数を拡張する。直和型の特定ケースで型引数が必要 とされない場合、その型引数を Nothing で置換できる。 105
+B] { def flatMap[C](f: B => Sum[A, C]): Sum[A, C] = this match { case Failure(v) => Failure(v) case Success(v) => f(v) } } final case class Failure[A](value: A) extends Sum[A, Nothing] final case class Success[B](value: B) extends Sum[Nothing, B]
Sum[A, C] と宣言でき、基底型は B について共変で、 A と C について反変です。B は共変として宣言されているので問題ありません。C は非 変なので問題ありません。一方、反変ポジションにおいて A は共変になっています。 よって Box で使用した解法を適用します。 sealed trait Sum[+A, +B] { def flatMap[AA >: A, C](f: B => Sum[AA, C]): Sum[AA, C] = this match { case Failure(v) => Failure(v) case Success(v) => f(v) } } final case class Failure[A](value: A) extends Sum[A, Nothing] final case class Success[B](value: B) extends Sum[Nothing, B]
の派生型でなければならないことを宣言し、 A >: Type は A が Type の基底型でなければならないことを宣言する文法です。 下記の例は Visitor かその派生型を保持することを可能にします。 case class WebAnalytics[A <: Visitor]( visitor: A, pageViews: Int, searchTerms: List[String], isOrganic: Boolean )