Upload
sanshiro-yoshida
View
4.984
Download
2
Embed Size (px)
Citation preview
Scalaz.effects
使い方など、簡単に説明します
よしだ(@halcat0x15a)
scalaz.effectsとは
● IOモナドやSTモナドなど、Haskellライクな入出力や可変状態を扱うパッケージ。
● package object effectsに関数やimplicitが定義され
ているので、それをimportすることでIOモナドやSTモナドが使える。
● Haskellの関数に対応しているので、"haskell 関数
名"でググればだいたい出てくる。
IOについて
● IO[A]は値であり、アクションといわれる。
● 関数はアクションを返すだけで、実行はしない。
● unsafePerformIOが呼ばれてはじめて実行される。
● 実行後、型Aの結果が返る。
主な標準入出力関数
● def putChar (c: Char): IO[Unit] ● def putStr (s: String): IO[Unit] ● def putStrLn (s: String): IO[Unit] ● def putOut [A] (a: A): IO[Unit]● def getChar : IO[Char] ● def readLn : IO[String]
主なIO[A]の主なメソッド
● def flatMap [B] (f: (A) ⇒ IO[B]): IO[B]● def map [B] (f: (A) ⇒ B): IO[B] ● def bracket [B, C] (after: (A) ⇒ IO[B])(during: (A) ⇒ IO[C]):
IO[C]○ このメソッドはduringを適用したあと、afterを呼びます。○ afterは例外に関係なく呼ばれます。
● def catchLeft : IO[Either[Throwable, A]] ● def except (handler: (Throwable) ⇒ IO[A]): IO[A] ● def unsafePerformIO : A
○ アクションを実行し、値を返します。
標準入出力関数の使い方
import scalaz._import effects._import Scalaz._
val i = for { _ <- putStrLn("Greetings! What is your name?") // 文字列を出力 inpStr <- readLn // 一行読み込む outStr = "Welcome to Scalaz, " |+| inpStr |+| "!" // 新しい文字列を作る _ <- putStrLn(outStr) // 作った文字列を出力} yield () // 戻り値はUnit
i.unsafePerformIO // ここで実行
結果
scala> :load io.scalaLoading io.scala...import scalaz._import effects._import Scalaz._i: scalaz.effects.IO[Unit] = scalaz.effects.IO$$anon$2@10f436Greetings! What is your name?SanshiroWelcome to Scalaz, Sanshiro!
標準の関数と比べて
● readLn >>= putStrLnとかできてカッコイイ!● 参照透過!
○ しかし、ScalaはIOを特別に見てくれるわけではないので、unsafePerformIOを書かなければいけない。
● 例外に対するメソッドがたくさんある。
● unsafePerformIOをタイプするのが面倒。
STについて
● 安全に可変状態を扱うためのもの。
● 可変参照がつくれる。
● 可変配列がつくれる。
● STモナド内でしか変更できない。
● runSTによってSTを実行し、値を取り出します。
STモナドのための主な関数
● def newVar [S, A] (a: A): ST[S, STRef[S, A]]
● def newArr [S, A] (size: Int, z: A)(implicit arg0: Manifest[A]): ST[S, STArray[S, A]]
● def returnST [S, A] (a: ⇒ A): ST[S, A]
● def runST [A] (f: Forall[[S]ST[S, A]]): A
STRefとSTArrayの主なメソッド
● STRef○ def write (a: ⇒ A): ST[S, STRef[S, A]] ○ def mod [B] (f: (A) ⇒ A): ST[S, STRef[S, A]] ○ def read : ST[S, A]
● STArray
○ def write (i: Int, a: A): ST[S, STArray[S, A]]○ def update [B] (f: (A, B) ⇒ A, i: Int, v: B): ST[S, Unit]○ def read (i: Int): ST[S, A] ○ def freeze : ST[S, ImmutableArray[A]]
STモナド(STRef)
import scalaz._import effects._import Scalaz._
val f = new Forall[({type X[S] = ST[S, String]})#X] { def apply[A] = for { v <- newVar[A, String]("Scala") // 参照を作る _ <- v.mod(_ |+| "Chan") // 値を取り出して文字を連結 s <- v.read // 値を取り出す } yield s}
runST(f) // 実行
結果
scala> :load st.scalaLoading st.scala...import scalaz._import effects._import Scalaz._f: java.lang.Object with scalaz.Forall[[S]scalaz.effects.ST[S,java.lang.String]] = $$$$2c9d7a9074166de3bf8b66cf7c45a3ed$$$$anon$1@c8354eres17: java.lang.String = ScalaChan
STモナド(STArray)
import scalaz._import effects._import Scalaz._import ImmutableArray._
val f = new Forall[({type X[S] = ST[S, ImmutableArray[String]]})#X] { def apply[A] = for { v <- newArr[A, String](5, "Scala") // 長さ5の配列を作り"Scala"で埋める _ <- v.write(1, "Clojure") // 1番目を"Clojure"に書き換え _ <- v.write(2, "JRuby") // 2番目を"JRuby"に書き換え _ <- v.update(_ |+| (_: String), 3, "Chan") // 3番目を取り出し、連結 a <- v.freeze // ImmutableArrayで取り出す } yield a}
runST(f).foreach(println) // 実行
結果
scala> :load st.scalaLoading st.scala...import scalaz._import effects._import Scalaz._import ImmutableArray._f: java.lang.Object with scalaz.Forall[[S]scalaz.effects.ST[S,scalaz.ImmutableArray[java.lang.String]]] = $anon$1@e5e138ScalaClojureJRubyScalaChanScala
利点は?
● HaskellではSTモナドを使えば安全に可変状態を扱えるみたいですが、Scalaではvarがあるので必要がない気がします。
● Forall書くのがめんどいです。
● newVar[RealWorld, A]みたいに書けばIO[A]に暗黙
的に変換してくれるので、unsafePerformIOで値を取り出せます。
● ScalaでSTモナドとはいったい・・・・・
ファイル入力(rReadLn)
import scalaz._import effects._import Scalaz._ val file = new java.io.File("test.txt")
val i = for { r <- bufferFile(file) // ファイルを開く _ <- { // ファイルの終わりまで再帰的に出力する関数 def f(i: IO[Option[String]]): IO[Unit] = i >>= { case Some(s) => putStrLn(s) >|> f(rReadLn(r)) case None => ().pure[IO] // 推論してくれないのが悲しい } f(rReadLn(r)) } _ <- closeReader(r) // 閉じる} yield () i.unsafePerformIO
ファイル入力(Iteratee)
import scalaz._import effects._import Scalaz._import IterV._
val file = new java.io.File("test.txt")
val i = for { xs <- for { // ファイルを読み込み、List[String]に変換 f <- getFileLines(file)(collect[String, List]) } yield f.run // IO[List[String]]が返る _ <- xs.traverse_(putStrLn) // リストの内容を出力} yield ()
i.unsafePerformIO
結果
scala> :load iter.scalaLoading iter.scala...import scalaz._import effects._import Scalaz._import IterV._file: java.io.File = test.txti: scalaz.effects.IO[Unit] = scalaz.effects.IO$$anon$2@cfb016The source for Scalaz is now hosted on GitHub - http://github.com/scalaz/scalaz
Scalaz is a library written in the Scala Programming Language. The intention of Scalaz is to include general functions that are not currentlyavailable in the core Scala API. The scalaz-core module depends only on the core Scala API and the core Java 2 Standard Edition API. Scalazis released under a BSD open source licence making it compatible with the licence of the Scala project.
Scalaz 6.0.1 was released in June 2011, targeting Scala 2.8.1 and 2.9.0.1.
IterVについて
● def collect [A, F[_]] (implicit mon: Monoid[F[A]], pr: Pure[F]): IterV[A, F[A]]
○ F[A]に変換します。○ 基本的にはこれを使うといいです。
● def drop [E] (n: Int): IterV[E, Unit] ○ n個の要素を消費します
● def head [E] : IterV[E, Option[E]] ○ 先頭要素を消費し、取得します
● def peek [E] : IterV[E, Option[E]] ○ 先頭要素を消費せず、取得します
● def length [E] : IterV[E, Int] ○ 要素全体の数を取得します。
Iterateeのもっと詳しい説明
● 続きはWebで☆
● スミマセン。LTでやれる気がしないです。● 詳しい解説はblogに書こうと思います。
Scalazでのファイル操作
● 入力に関してはbufferFileなどを直接使わずに、Iterateeな関数を使うと良いです。
● getFileLinesは便利。closeは関数内でしている。
● なぜか出力の関数はハブられてる。
● closeWriterぐらいはあってもいいのではないか・・・・
● まだいろいろと足りないイメージ。
参考資料など
● Iterateeについて○ Scalaz Tutorial: Enumeration-Based I/O with
Iteratees○ Lazy I/O must go! - Iteratee: 列挙ベースのI/O
● 本
○ Real World Haskellーー実戦で学ぶ関数型言語プログラミング