ElixirでMonad、モナド (1)

Elixirの型クラス周りのライブラリである  [Algae](https://github.com/expede/algae) について紹介します
あくまで使い方について紹介するだけでモナドについての難しい話はしません

# Algae
代数的データ型とそれに対する型クラスインスタンスの定義を含むライブラリです
Elixirの世界にMaybeやEitherをもたらしてくれるのでカジュアルに使っても
nilセーフになったり、パターンマッチヘルなどを回避できるなど恩恵があります

今回では定義されている代数的データや、defmacroで定義されているdo構文をみていきましょう

## 依存性
Algaeは前項で述べたとおり代数的データ型とそれに対する型クラスインスタンスの定義のライブラリです
型クラス機能自体の実装は [type_class](https://github.com/expede/type_class)
各型クラス(Functor, Applicative, Monad. etc...)の実装は [Witchcraft](https://github.com/expede/witchcraft)
にあります

## do構文
do構文はWitchcraftの `lib/witchcraft/chain.ex` に定義されています
Haskellと違い `Chain` 型クラスを実装していれば使用することができます
(ただし、returnの使い方が後述のmonadマクロを使用する上、少し特殊です)

```elixir
import Witchcraft.Chain
alias Algae.Maybe
alias Algae.Maybe.{Just, Nothing}

def f(nillable, n) do
  chain do
    a <- Maybe.from_nillable(nillable)
    b <- safe_div(a, n)
    Maybe.new(a + b)
  end
end

def safe_div(a, b) do
  if b != 0, do: %Just{just: a / b}, else: %Nothing{}
end
```

```elixir
iex(31)> f(1, 3)
%Algae.Maybe.Just{just: 1.3333333333333333}
iex(32)> f(nil, 3)
%Algae.Maybe.Nothing{}
iex(33)> f(1, 0)
%Algae.Maybe.Nothing{}
iex(34)>
```

また、`lib/witchcraft/monad.ex` に定義されているmonad macroを使うことでreturnを使用することも可能になります

```elixir
def f(nillable, n) do
  monad Maybe.from_nillable(nil) do
    a <- Maybe.from_nillable(nillable)
    b <- safe_div(a, n)
    return (a + b)
  end
end
```
monad macro第一引数として渡している `Maybe.from_nillable` は `return` を呼び出したときに実際に呼び出される関数となります、`from_nillable` に渡している引数値は無視されるのでなんでもいいのですが、なんらかの値を渡さない場合は `Maybe.from_nillable/0` の関数として認識されてしまうので必須です
returnにnil値を渡さないとわかっているのであれば `Maybe.new()` でも良いでしょう

do構文を使わずbind関数で書く場合はこうなります
(Haskellのbind (`>>=`) とは違い、bindのaliasは `>>>` になっています)

```elixir
def f(nillable, n) do
  Maybe.from_nillable(nillable)
  >>> fn a -> safe_div(a, n)
  >>> fn b -> Maybe.new(a + b) end end
end
```
…見ての通りネストするほど `end` が多くなっていくのでdo構文で書くことをオススメします

ちなみに `let` による変数束縛もちゃんと使えます

```elixir
def f(nillable, n) do
  chain do
    a <- Maybe.from_nillable(nillable)
    b <- safe_div(a, n)
    let c = 10
    Maybe.new(a + b + c)
  end
end
```

## Maybe
- Haskell

```Haskell
data Maybe a = Just a 
             | Nothing
```

- Elixir(Algae)

```elixir
defsum do
  defdata Nothing :: none()
  defdata Just    :: any()
end
```
`defsum` は直和 `defdata` は直積を定義するためのマクロです
Elixirには型パラメタの機能はないので `Maybe a` にあたる `a` は `any` になります

- `Maybe.new/0` `Maybe.new/1` / `Maybe.from_nillable/1`
コンストラクタとしては二種類用意されています
`Maybe.new(nil)` とした場合は `%Just{just: nil}` になってしまうので基本的には `Maybe.from_nillable` を使うことを推奨します


- `Maybe.from_maybe/2`
第一引数に `Maybe` 型を受け取りJustの場合はその中身を、Nothingの場合は第二引数の値を返します

## Either
- Haskell

```Haskell
data Either a b = Left a
                | Right b
```

```Elixir
defsum do
  defdata Left  :: any()
  defdata Right :: any()
end
```

Elixirには型パラメタの機能はない(ry

- `Right.new/1` / `Left.new/1`
Eitherにおけるコンストラクタです

- example

```elixir
def fE(nillable, n) do
  chain do
    a <- case nillable do
           nil -> Left.new("nil is can't division.")
           n -> Right.new(n)
         end
    b <- safe_divE(a, n)
    Right.new(a + b)
  end
end

def safe_divE(a, b) do
  if b != 0, do: Right.new(a / b), else: Left.new("n is 0.")
end
```

```elixir
iex(50)> fE(1, 3)
%Algae.Either.Right{right: 1.3333333333333333}
iex(51)> fE(nil, 3)
%Algae.Either.Left{left: "nil is can't division."}
iex(52)> fE(1, 0)
%Algae.Either.Left{left: "n is 0."}
```

## State

```Haskell
newtype State s a = State { runState :: s -> (a,s) }  
```

```elixir
@type runner :: (any() -> {any(), any()})
@type t :: %State{runner: runner()}
```

Maybe/Eitherと比較するとかなり特殊ですね
Stateの実態は、状態値 `s` を受け取って結果値 `a` と新しい状態 `s` を返す関数なのでこのような特殊な形になります
HaskellではStateを渡すときに `runState` で渡しますがAlgaeでは `runner` に(実態の関数に)渡します

```elixir
Algae.State.state(fn x -> {x + 1, x} end).runner.(42)
```

- `State.new/1` / `State.state/1`
`any() -> {any(), any()}` の関数を受け取ってState型を返す
つまりコンストラクタです
state関数はnew関数のエイリアスです

- `put/1` / `get/0`
Stateモナドコンテキスト内で使える値のセットとゲットです
と書くよりdo構文で使っているのを見るほうが理解できるでしょう

```elixir
def fS() do
  init = State.state(fn s -> {0, s} end)
  chain do
    i <- init
    n <- State.get()
    State.put(99) ~> (fn _z -> i + n end)
  end
end
```

```elixir
iex(65)> A.fS().runner.(3)
{3, 99}
```
( `~>` はfmap( `<$>` )です)

その他
状態を変更する関数を受け取る `modify`
Stateモナドから結果値のみを取得する `evaluate`
Stateモナドから状態のみを取得する `execute` (使い道あるのかな？)
などがありますが割愛します

---

今回紹介した他 `writer` `reader` `free` などおなじみのモナドたちが定義されていますが
そちらの紹介はまた次の機会という形にします

Haskellのように強い型付けがないのでElixirの世界ではモナドを利用したとしても
完全に副作用の切り離しを保証できるものではありません
ですがdo構文などコードを完結に書く力は得られるので使う価値のないものでもありません
Elixirの世界にモナドを取り入れられるこのライブラリを僕は素晴らしいものだと思います