23
Scalaz.effects 使い方など、簡単に説明します よしだ(@halcat0x15a)

Scalaz effects

Embed Size (px)

Citation preview

Page 1: Scalaz effects

Scalaz.effects

使い方など、簡単に説明します

よしだ(@halcat0x15a)

Page 2: Scalaz effects

scalaz.effectsとは

● IOモナドやSTモナドなど、Haskellライクな入出力や可変状態を扱うパッケージ。 

● package object effectsに関数やimplicitが定義され

ているので、それをimportすることでIOモナドやSTモナドが使える。

● Haskellの関数に対応しているので、"haskell 関数

名"でググればだいたい出てくる。

Page 3: Scalaz effects

IOについて

● IO[A]は値であり、アクションといわれる。

● 関数はアクションを返すだけで、実行はしない。

● unsafePerformIOが呼ばれてはじめて実行される。

● 実行後、型Aの結果が返る。 

Page 5: Scalaz effects

主な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

○ アクションを実行し、値を返します。

Page 6: Scalaz effects

標準入出力関数の使い方

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 // ここで実行

Page 7: Scalaz effects

結果

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!

Page 8: Scalaz effects

標準の関数と比べて

● readLn >>= putStrLnとかできてカッコイイ!● 参照透過!

○ しかし、ScalaはIOを特別に見てくれるわけではないので、unsafePerformIOを書かなければいけない。

● 例外に対するメソッドがたくさんある。

● unsafePerformIOをタイプするのが面倒。 

Page 9: Scalaz effects

STについて

● 安全に可変状態を扱うためのもの。

● 可変参照がつくれる。

● 可変配列がつくれる。

● STモナド内でしか変更できない。 

● runSTによってSTを実行し、値を取り出します。

Page 12: Scalaz effects

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) // 実行

Page 13: Scalaz effects

結果

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

Page 14: Scalaz effects

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) // 実行

Page 15: Scalaz effects

結果

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

Page 16: Scalaz effects

利点は?

● HaskellではSTモナドを使えば安全に可変状態を扱えるみたいですが、Scalaではvarがあるので必要がない気がします。

● Forall書くのがめんどいです。

● newVar[RealWorld, A]みたいに書けばIO[A]に暗黙

的に変換してくれるので、unsafePerformIOで値を取り出せます。

● ScalaでSTモナドとはいったい・・・・・

Page 17: Scalaz effects

ファイル入力(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

Page 18: Scalaz effects

ファイル入力(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

Page 19: Scalaz effects

結果

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.

Page 20: Scalaz effects

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] ○ 要素全体の数を取得します。

Page 21: Scalaz effects

Iterateeのもっと詳しい説明

● 続きはWebで☆

● スミマセン。LTでやれる気がしないです。● 詳しい解説はblogに書こうと思います。

Page 22: Scalaz effects

Scalazでのファイル操作

● 入力に関してはbufferFileなどを直接使わずに、Iterateeな関数を使うと良いです。

● getFileLinesは便利。closeは関数内でしている。

● なぜか出力の関数はハブられてる。

● closeWriterぐらいはあってもいいのではないか・・・・ 

● まだいろいろと足りないイメージ。

Page 23: Scalaz effects

参考資料など

● Iterateeについて○ Scalaz Tutorial: Enumeration-Based I/O with

Iteratees○ Lazy I/O must go! - Iteratee: 列挙ベースのI/O

● 本

○ Real World Haskellーー実戦で学ぶ関数型言語プログラミング