Erlangでモナド
ビヘイビアを使ってmonad型クラスのようなものを提供。monadのインスタンスを作りたい人はreturn/1とbind/2を用意すればよいようにしている。
monad.erl
-module(monad).
-export([flatten/2, map/3, sequence/2, mapM/3]).
-type m(_A) :: term().
-callback return(A) -> m(A) when A :: term().
-callback bind(m(A), fun((A) -> m(B))) -> m(B).
-spec flatten(module(), m(m(A))) -> m(A) when
A :: term().
flatten(Instance, Mmx) ->
Instance:bind(Mmx, fun(M) -> M end).
-spec map(module(), fun((A) -> B), m(A)) -> m(B) when
A :: term(),
B :: term().
map(Instance, F, M) ->
Instance:bind(M, fun(X) -> Instance:return(F(X)) end).
-spec sequence(module(), list(m(A))) -> m(list(A)) when
A :: term().
sequence(Instance, []) ->
Instance:return([]);
sequence(Instance, [M | Ms]) ->
Instance:bind(M, fun(Hd) ->
Mtl = sequence(Instance, Ms),
Instance:bind(Mtl, fun(Tl) ->
Instance:return([Hd | Tl])
end) end).
-spec mapM(module(), fun((A) -> m(B)), list(A)) -> m(list(B)) when
A :: term(),
B :: term().
mapM(Instance, F, Xs) ->
sequence(Instance, lists:map(F, Xs)).
モナドのインスタンス例
resultモナド
{ok, _}
または {error, _}
のような形をしたデータ型をresult
型と呼ぶことにする。
OCamlでいう('a, exn) result
型、Scalaでいう Try[A]
型。
Erlangではこういう形のデータを想定した処理がたくさんあるらしいので、モナディックに結合したいので↑のmonad
ビヘイビアを使ってモナドにする。
result.erl
-module(result).
-behavior(monad).
-export([return/1, bind/2]).
-export_type([result/1]).
-type result(A) :: {ok, A} | {error, term()}.
-spec return(A) -> result(A) when A :: term().
return(X) -> {ok, X}.
-spec bind(result(A), fun((A) -> result(B))) -> result(B) when
A :: term(),
B :: term().
bind({error, Reason}, _F) -> {error, Reason};
bind({ok, X}, F) -> F(X).
使用例:
Erlangにはまだdo構文相当がないようなので、以下のようにbindでつなげる。
sample.erl
main() ->
result:bind( find_parameter(), fun(Param) ->
result:bind( take_user_id(Param), fun(UserId) ->
result:bind( find_user(UserId), fun(User) ->
result:return(User)
end) end) end).
(find_parameter/0
, take_user_id/1
, find_user/1
はそれぞれ失敗するかもしれない架空の関数)
do構文 (Scalaのfor) がわかる人は以下のように心の目で置き換えて読むのがよい。
sample.erl
main() ->
result:do
Param <- find_parameter(),
UserId <- take_user_id(Param),
User <- find_user(UserId),
return (User)
end.