今日はScalazについて話します。
JIT(Just In Tsukkomi)は大歓迎。
いま話題のScalaz!
まあ、コードを見ればわかるのではないか?
scala> import scalaz._
import scalaz._
scala> import Scalaz._
import Scalaz._
scala>
簡単そうだね!
scala> 1 |+| 1
res0: Int = 2
scala> 'A === 'A
res1: Boolean = true
scala> 1.0.some
res2: Option[Double] = Some(1.0)
scala>
(;゚Д゚)!?
どんなものに変換されているのだろう?
scala> (1: { def some: Option[Int] }).getClass
res16: java.lang.Class[_ <: AnyRef] = class scalaz.Identity$$anon$1
どうやらIdentityとやらに変換されている模様
scala> (1: Identity[Int])
res19: scalaz.Identity[Int] = 1
scala> res19.
/== === ?? ?|? asInstanceOf assert_=== assert_≟ canEqual cons
constantState doWhile dual equal equalA equalBy fail failNel gt
gte isInstanceOf iterate leaf left logger lt lte mapply
matchOrZero max min node ok pair print println pure
pureUnit repeat replicate right set show shows snoc some
squared state success successNel text toString unfold unfoldTree unfoldTreeM
unit value whileDo wrapNel zipper |+| |> η σ
≟ ≠ ⊹
scala> res19.
なんだか変な名前のメソッドがいっぱいですね!
scala> case class ScalaChan()
defined class ScalaChan
scala> ScalaChan() === ScalaChan()
<console>:16: error: could not find implicit value for parameter e: scalaz.Equal[ScalaChan]
ScalaChan() === ScalaChan()
^
scala>
Equal[ScalaChan]のimplicit valueが見つからない。
sealed trait Identity[A] extends Equals with IdentitySugar[A] {
def value: A
def ===(a: A)(implicit e: Equal[A]): Boolean = e equal (value, a)
trait Equal[-A] {
def equal(a1: A, a2: A): Boolean
}
シンプル!
このEqualというのは一般的に型クラスと呼ばれるものです。
型クラスはある型に対して性質を定義することができます。
Equalは同値比較をするための型クラスです。
scala> implicit lazy val ScalaChanEqual: Equal[ScalaChan] = new Equal[ScalaChan] {
| def equal(a1: ScalaChan, a2: ScalaChan): Boolean = a1 == a2
| }
ScalaChanEqual: java.lang.Object with scalaz.Equal[ScalaChan]
scala> ScalaChan() === ScalaChan()
res21: Boolean = true
scala> Identity(ScalaChan()).===(ScalaChan())(ScalaChanEqual)
res22: Boolean = true
scala>
ScalaChanEqualを作ったことでScalaChanも===が利用できるようになりました。
scala> implicit lazy val ScalaChanShow: Show[ScalaChan] = new Show[ScalaChan] {
| def show(a: ScalaChan): List[Char] = "Scalaちゃん".toList
| }
ScalaChanShow: scalaz.Show[ScalaChan] = <lazy>
scala> ScalaChan().shows
res0: String = Scalaちゃん
scala> Identity(ScalaChan()).shows(ScalaChanShow)
res1: String = Scalaちゃん
scala>
このようにScalazではimplicit valueを定義していくことで、利用できる関数が増えていきます。
必要最低限のものを定義するだけで高度な関数を利用可能
すべての要素にある値を加算する
scala> def mapAppend(s: Seq[Int])(i: Int): Seq[Int] = s.map(_ + i)
mapAppend: (s: Seq[Int])(i: Int)Seq[Int]
scala> mapAppend(List(1, 2, 3))(5)
res0: Seq[Int] = List(6, 7, 8)
scala> mapAppend(Vector(1, 2, 3))(5)
res1: Seq[Int] = Vector(6, 7, 8)
scala>
Int以外にDoubleやStringも対応させたい。
結合二項演算が必要。
scala> def mapAppend[A: Semigroup](s: Seq[A])(a: A): Seq[A] = s.map(_ |+| a)
mapAppend: [A](s: Seq[A])(a: A)(implicit evidence$1: scalaz.Semigroup[A])Seq[A]
scala> mapAppend(List(1, 2, 3))(5)
res2: Seq[Int] = List(6, 7, 8)
scala> mapAppend(List("Hello", "Real"))("World")
res3: Seq[java.lang.String] = List(HelloWorld, RealWorld)
scala>
Seq以外にも対応したい。
各要素に関数を適用する関数(map)が必要。
scala> def mapAppend[M[_]: Functor, A: Semigroup](m: M[A])(a: A): M[A] = m.map(_ |+| a)
mapAppend: [M[_], A](m: M[A])(a: A)(implicit evidence$1: scalaz.Functor[M], implicit evidence$2: scalaz.Semigroup[A])M[A]
scala> mapAppend(List(1, 2, 3))(5)
res4: List[Int] = List(6, 7, 8)
scala> mapAppend(Option("Hello"))("World")
res5: Option[java.lang.String] = Some(HelloWorld)
scala>
def mapAppend[M[_], A](m: M[A])(a: A)(implicit f: Functor[M], s: Semigroup[A]) =
maImplicit(m).map(x => (mkIdentity(x) |+| a)(s))(f)
mapAppend[Option, String](
Option("Hello")
)(
"World"
)(
Functor.OptionFunctor, Semigroup.StringSemigroup
)
ScalazオブジェクトにMix-inされる。
Scalazのドキュメントはこれらを抑えておけば読めるようにます。
def mapAppend[M[_]: Functor, A: Semigroup](m: M[A])(a: A) = m.map(_ |+| a)
def mapAppend[M[_], A](m: M[A])(a: A)(implicit f: Functor[M], s: Semigroup[A]) =
m.map(_ |+| a)
def mapAppend[M[_], A](m: M[A])(a: A)(implicit f: Functor[M], s: Semigroup[A]) =
m.map(x => x |+| a)
def mapAppend[M[_], A](m: M[A])(a: A)(implicit f: Functor[M], s: Semigroup[A]) =
maImplicit(m).map(x => mkIdentity(x) |+| a)
def mapAppend[M[_], A](m: M[A])(a: A)(implicit f: Functor[M], s: Semigroup[A]) =
maImplicit(m).map(x => (mkIdentity(x) |+| a)(s))(f)
mapAppend(List(1, 2, 3))(5)(Functor.TraversableFunctor, Semigroup.IntSemigroup)
mapAppend(Option("Hello"))("World")(Functor.OptionFunctor, Semigroup.StringSemigroup)