%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% @doc The Traversable Type Class. %%% @end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%_* Module declaration ====================================================== -module(do_traversable). %%%_* Exports ================================================================= -define(API, [ sequence/2, sequencez/2, traverse/2]). -export(?API). -ignore_xref(?API). %%%_* Includes ================================================================ -include("do_guards.hrl"). -include("do_types.hrl"). %%%_* Macros ================================================================== -define(LAZY, lazy). -define(THUNK, thunk). -define(DEFAULT, default). %%%_* Code ==================================================================== %%%_* API --------------------------------------------------------------------- -spec sequence(traversable(applicative(A)), atom()) -> applicative(traversable(A)). sequence(Traversable, Mod) -> sequence(Traversable, Mod, ?DEFAULT). -spec sequencez(traversable(fn(applicative(A))), atom()) -> applicative(traversable(A)). sequencez(Traversable, Mod) -> sequence(Traversable, Mod, ?LAZY). -spec traverse(fn(A, applicative(B)), traversable(A)) -> applicative(traversable(B)). traverse(F, Traversable) when ?isF1(F) -> do:fmap(F, Traversable). %%%_* internal ---------------------------------------------------------------- sequence([F | Rest], Mod, ?LAZY) when ?isF0(F) -> sequence([F() | Rest], Mod, ?THUNK); sequence([Elm | Rest], Mod, Mode) -> Applicative = Mod:fmap(fun(Val) -> fun(Vals) -> [Val | Vals] end end, Elm), Mod:liftA2(Applicative, sequence(Rest, Mod, reset(Mode))); sequence([], Mod, _Mode) -> Mod:pure([]); sequence(Map, Mod, Mode) when is_map(Map) -> {Keys, Vals} = lists:unzip(maps:to_list(Map)), Mod:fmap(fun(Sequenced) -> maps:from_list(lists:zip(Keys, Sequenced)) end, sequence(Vals, Mod, Mode)). reset(?THUNK) -> ?LAZY; reset(?DEFAULT) -> ?DEFAULT. %%%_* Tests =================================================================== -ifdef(TEST). -include_lib("eunit/include/eunit.hrl"). traverse_test() -> F = fun(A) -> A + 1 end, ?assertEqual([1, 2, 3], traverse(F, [0, 1, 2])), ?assertEqual(#{a => 1, b => 2, c => 3}, traverse(F, #{a => 0, b => 1, c => 2})). -endif.