scalaz - IOモナド

[独習Scalaz - IOモナド](http://eed3si9n.com/learning-scalaz/ja/IO-Monad.html)を呼んだときのメモ。

IOモナドは入出力などの副作用を扱うためのモナドだ。

## 定義

```scala
sealed trait IO[A] {
  private[effect] def apply(rw: Tower[IvoryTower]): Trampoline[(Tower[IvoryTower], A)]
```

STに似ている。

## 使い方

IOのapplyが名前渡し(by-name)になっているのですぐには実行されない。

```scala
val io: IO[Unit] = IO(println(1))
```

実行するには`unsafePerformIO`を使う。

```scala
io.unsafePerformIO // 1
```

ちなみにprintlnするなら`IO.putStrLn`が用意されている。

次にファイルを出力してみる。

```scala
val io: IO[Unit] = for {
  filename <- IO(scala.io.StdIn.readLine())
  content <- IO(scala.io.Source.fromFile(filename).getLines().toList)
} yield {
  println(content.map(_ + "!"))
}
```

forで書くと、より分かりやすい。

## Stdな関数

Stdの関数がいくつか用意されている。

```
getChar // readChar
putChar // print(char)

putStr // print(x)
putStrLn // println(x)

readLn // readLine
put // print(show)
putLn // println(show)
```

## IOインスタンスの関数

IOはmap/flatMap/zipなどの関数を持っている。一通り試してみる。

zip

```scala
IO.put(1) unsafeZip IO.put("a") // IO((1, "a"))
```

zipWith

```scala
IO("1").unsafeZipWith[String, String](IO("a"), _ + _) // IO(1a)
```

map

```scala
IO(1).map(_.toString) // IO("1")
```

flatMap

```scala
IO(1).flatMap(IO.put(_)) // IO(put(1))
```

liftIO : liftできる。id/option/listなどが用意されている。

```scala
IO(1).liftIO[({type λ[A]=OptionT[IO, A]})#λ] // OptionT[IO, Int]
```

except : 例外をハンドルできる。

```scala
IO(print("a".toInt)).except((e: Throwable) => IO(print(e)))
```

catchSome : 2つの関数をとる。
1つ目の関数で例外をハンドルしてOptionを返す。
Someの場合2つ目の関数でIOを返す。
Noneの場合例外が飛ぶ。

```scala
IO(println("a".toInt)).catchSome[String]({e => e.toString.some}, {msg => IO.putStr(msg)})
```

catchLeft : 上記のValidation版

```scala
IO(print("a".toInt)).catchLeft // \/[Throwable,Unit]
```

catchSomeLeft : catchLeft + catchSome

```scala
IO(print("a".toInt)).catchSomeLeft((e: Throwable) => e.toString.some)
```

onException : 例外時のIOを定義できる。

```scala
IO(print("a".toInt)).onException(IO.putStrLn("err!"))
// unsafePerformIOすると、err!が出力されて例外が吐かれる。
```

bracket : try-finallyのようなもの。関数を2つとる。
2つ目がまず実行される。(try)
1つ目は成功/失敗どちらでも実行される。(finally)

```scala
def bracket[B, C](after: A => IO[B])(during: A => IO[C]): IO[C]
```

```scala
IO("a").bracket[Unit, Int]({s => IO.putStrLn("finally " + s)})({s => IO(s.toInt)})
```

ensuring : finally

```scala
IO("a".toInt).ensuring(IO.put("finally!"))
```

using : ローンパターン (closeしてくれる)

```scala
import java.io._

implicit val bfrResource = Resource.resourceFromCloseable[BufferedReader]

IO(new BufferedReader(new FileReader("/tmp/a"))).using { bf =>
  IO.putStr(bf.readLine())
}
```

## IOオブジェクトの関数

newIORef : IO[IORef[A]]を返す。
IORefはread/write/modを持っている。内部ではSTを使って値を更新している。

```scala
for { 
  ref <- IO.newIORef(1)
  _   <- ref.write(2)
  _   <- ref.mod(_ + 1)
  r   <- ref.read
} yield r // IO(3)
```

throwIO : 例外を投げるIO。

```scala
IO.throwIO(new NoSuchElementException("foo"))
```

idLiftControl : TODO

```scala
IO.idLiftControl[Option, Int] { _[Int](1.some).flatten } // Some(1)
```

controlIO : TODO

```scala
TODO
```

onExit : RegionTを返す。TODO

```scala
IO.onExit[IvoryTower, ({type P[A]=OptionT[IO, A]})#P](IO.put(1)) // RegionT[IvoryTower, OptionT[IO, Int], FinalizerHandle[...]
```

runRegionT : RegionTを実行する。

```scala
TODO
```

IOToST : IOをSTに変換する。(implicit)

```scala
val st: ST[IvoryTower, Int] = IO(1)
// STからIOにもできる。同様にSTにimplicitが定義してある。
val io: IO[Int] = st
```

ioUnit : IO(()) を返す。

```scala
IO.ioUnit // IO(())
```

MonadControlIOとRegionTが理解できてないので、TODOになってる。

scalazのMonadControlIOはどうやって使うんだろうか。MonadIOみたいにimplicitが用意されてれば使えるんだが...自分で定義すればいいんだろうけど、そもそもMonadControlIOの意味が理解できてないから、やはりTODOだ。

しかしscalazはコメントやUTがない関数が多い...。

haskellにも大体同じ関数あるので、hoogleするが..。