【YAPC::Hokkaido 2016 前夜祭】 From Perl to Haskell

# まだ型のない言語で消耗してるの？

----

# 静的型付け言語を使おう！

----

## Haskellを触ったことがある人にありがちなこと

* リスト とか `map` とか `fold` とか `zip` だけやたら詳しい
* REPLしか触ったことがない
* ファンクタ、モナドとはなにかググり始めてしまった

----

# Haskellはプログラミング言語です！

----

# 実用言語としてのHaskell

* 高度に抽象な概念はデザパタやビルトイン機能のようなもの
* プログラムを書けるようになってから振り返ったほうが学習効率が良い

----

# 開発環境

* [Stack](https://www.haskellstack.org/)
    * いい感じに色々裏でやってくれるビルドツール
    * コンパイラGHCのインストールも任せたほうがいい
* [Intero](https://commercialhaskell.github.io/intero/)
    * emacs をいい感じに使えるようにするモード
    * stackによる自動セットアップ
    * 補完とかジャンプとかflymakeとか
    * vim使ってる人はいませんよね？

----

# ライブラリ

* [stackage LTS](https://www.stackage.org/lts)
    * stackで依存でハマらずにビルドできるライブラリ群
* [hackage](http://hackage.haskell.org/)
    * stackageにないものもある
    * 依存の解決は自己責任

----

# ドキュメント

* [GHC](https://downloads.haskell.org/~ghc/latest/docs/html/) のマニュアル
* [haskell-lang.org](https://haskell-lang.org/)
* 保守されてないかも？
    * [haskell.org](https://haskell.org/)
    * [wiki.haskell.org](https://wiki.haskell.org/Haskell)
* [lotz84さんとこ](https://github.com/lotz84/haskell)
    * 雑多なブックマーク的なの。メンテされてる。

----

# カジュアルな実行

* `stack runghc hello.hs`

```haskell
main = putStrLn "Hello!"
```

または

```
$ echo 'main = putStrLn "Hello!"' | stack runghc
Hello!
```

----

# Perlの関数の型

```perl
sub div_mod {
    my ($x, $y) = @_;
    my $div;
    {
        use integer;
        $div = $x / $y;
    }
    ($div, $x - $div*$y);
}
```

----

# Perlの関数の型

Perl の全ての関数は副作用を書けるので、戻り値を `IO` で包む。

```haskell
perlDivMod :: Int -> Int -> IO (Int, Int)
perlDivMod x y = do
  let d = x `div` y
  return (d, x - d * y)
```

----

# `IO` を恐れない

`IO` を使わない純粋な関数は奨励されるが、以下を覚悟する。覚悟できないなら `IO` を恐れず使う。

* `print` デバグができない
* 設定ファイルを読めない
* 環境変数を読めない
* ログを吐けない
* 現在時刻を取得できない
* `IO` を使うよう定義してしまった自前の関数は一切呼べない
* `IO` を使わないと決めた関数から呼び出す予定のすべての関数が、これらの制約をすべて受ける

----

# `do` 記法

Haskellを手続き型言語に変える糖衣構文。

* `IO` 型を並べると、順に実行してくれる
* `IO` 型に他のモナドを被せた、「`IO`」が可能なモナドも同じように使え、多用される
    * 例: `ReaderT Context IO`、 `StateT State (ReaderT Context IO)` など
    * `liftIO` で `IO` の呼び出しが可能
* `IO` 型以外を記述した場合の振る舞いには要注意

----

# `IO` 関数の戻り値を使う場合の注意

```perl
sub fizz { "Fizz" }

sub buzz { "Buzz" }

sub fizz_buzz { fizz . buzz }

print fizz_buzz;
```

----

# `IO` 関数の戻り値を使う場合の注意

`IO` 型では、関数の戻りとを直接 `<>` の引数として使う `fizz <> buzz` のような記述はできない。面倒でも `<-` で `IO` を剥がす。

```haskell
{-# LANGUAGE OverloadedStrings #-}
import Data.Monoid ((<>))
import qualified Data.Text.IO as Text

fizz = return "Fizz"

buzz = return "Buzz"

fizzBuzz = do
    fizzStr <- fizz
    buzzStr <- buzz
    return $ fizzStr <> buzzStr

main = do
  fizzBuzzStr <- fizzBuzz
  Text.putStrLn fizzBuzzStr
```

----

# `IO` 関数の戻り値を使う場合の注意

`<$>` `<*>` `=<<` 関数を使いこなせる人ならこう。

```haskell
{-# LANGUAGE OverloadedStrings #-}
import Data.Monoid ((<>))
import qualified Data.Text.IO as Text

fizz = return "Fizz"

buzz = return "Buzz"

fizzBuzz = (<>) <$> fizz <*> buzz

main = Text.putStrLn =<< fizzBuzz
```

が、格好つけて書こうとするとハマりやすい。

----

# ミュータブル変数

```perl
sub is_divisible_by {
    my ($x, $y) = @_;

    my $divisible = 0;
    $divisible = 1 if $x % $y == 0;

    return $divisible;
}

print is_divisible_by(8, 7) ? "Divisible by 7\n" : "Not divisible by 7\n";
```

----

# ミュータブル変数

イミュータブル変数で定義し直す。

`done'` という別の変数に変更後の値を入れている。

```haskell
isDivisibleBy x y = do
  let divisible = False
      divisible' = if x `mod` y == 0 then True
                                     else divisible
  return divisible'

main = do
  divisible <- isDivisibleBy 8 7
  putStrLn $ if divisible then "Divisible by 7"
                          else "Not divisible by 7"
```

----

# ループ

* 再帰で書き直す
* `last` は再帰をやめる
* `next` は後続の処理をせずに再帰する
* ループ中に変化する状態は、引数と戻り値で表す

----

# ループ

```perl
my $n = 0;
while (<>) {
    chomp;
    last if $_ eq 'last';
    next if $_ eq 'next';
    print ++$n, ": $_\n";
}

print "Got ", $n, " lines.\n"
```

----

# ループ

```haskell
import Data.Monoid ((<>))
import System.IO (isEOF)

main = do
  n <- loop 0
  putStrLn $ "Got " <> show n <> " lines."
  where
    loop n = do
      l <- getLine'
      case l of
        Nothing     -> return n
        Just "last" -> return n
        Just "next" -> loop n
        Just l'     -> do let n' = n + 1
                          putStrLn $ show n' <> ": " <> l'
                          loop n'

    -- helper
    getLine' = do
      eof <- isEOF
      if eof then return Nothing
             else Just <$> getLine
```

----

# 遅延リストによるI/Oという幻想

* 遅延リストに `map` `filter` `fold` でI/Oを美しく扱えるというのは幻想
* `IO [String]` では入力ストリームは表せない
    * 一回の `IO` で遅延リスト1個を作れるという型
    * ファイルを読む(I/O)ことを何度も遅延して行うのは無理
    * `unsafeInterleaveIO` という邪悪な何かが必要
* ストリームライブラリが必要

----

# 遅延リストによるI/Oという幻想

```haskell
import Data.Monoid ((<>))
import Pipes ((>->))
import qualified Pipes.Prelude as Pipes

main = do
  n <- foldM' step 0
     . filter' (/= "next")
     . takeWhile' (/= "last")
     $ Pipes.stdinLn
  putStrLn $ "Got " <> show n <> " lines."
  where
    step n s = do
      let n' = n + 1
      putStrLn $ show n' <> ": " <> s
      return n'

    -- helper functions
    foldM'     f a = Pipes.foldM f (return a) return
    takeWhile' p   = (>-> Pipes.takeWhile p)
    filter'    p   = (>-> Pipes.filter    p)
```

----

## グローバル変数

* アプリを作ろうとすると、自然とグローバル変数が出てくる
    * アプリの設定とか
    * ロガーとか
    * そういう諸々のシングルトンインスタンスとか
* `ReaderT Ctx IO` というモナドの元でアプリを作るのが定石

----

## グローバル変数

```perl
my $counter = 0;

sub count {
    print "count ", ++$counter, "\n";
}

count;
count;
```

----

## グローバル変数

* アプリ内で共有したい状態( `$count` など)はまとめて `data` 定義
* ミュータブルな値は `IORef` で定義するが、`main` 内で初期化が必要
* `ReaderT Ctx IO` モナド下では、 `asks` 関数でいつでも自由に `Ctx` 内の変数に触れる
* `IO` も`liftIO` で任意の箇所で可能

----

## グローバル変数

```haskell
import Data.IORef (IORef, newIORef, modifyIORef, readIORef)
import Control.Monad.Trans.Reader (runReaderT, asks)
import Control.Monad.IO.Class (liftIO)

data Ctx = Ctx { counter :: IORef Int }

main = do
  ref <- newIORef 0
  runReaderT main' $ Ctx { counter = ref }

count = do
  ref <- asks counter
  liftIO $ do
    modifyIORef ref (+ 1)
    n <- readIORef ref
    putStrLn $ "count " ++ show n

main' = do
  count
  count
```

----

## まとめ

* Haskellで普通のアプリを書くためのTipsを紹介した
    * `IO` を恐れず、格好つけずに`<-` を使う
    * ミュータブル変数は代入箇所をイミュータブルで定義し直す
    * ループは再帰で
    * 状態を持つアプリを作るときは `ReaderT Ctx IO` が定石
* CONS:
    * `<-` による関数呼出しが必須なので冗長になりがち
    * `next` や `last` を愚直に再帰で表すと冗長になりがち
    * `IORef` によるミュータブル変数は冗長になりがち
* PROS:
    * まったく型を書いてないが、型安全が保証されている
    * 最初は冗長でも、抽象概念を学べば学ぶほどコードは短くなっていく