%% @doc %% category pattern: option category -module(datum_cat_option). %% (/=) -export(['/='/1]). %% (.) operation -export(['.'/3, chain/1, curry/1]). %% category transformers -export([unit/1, fail/1, require/3, sequence/1, flatten/1, optionT/1, eitherT/1]). %% %% '/='(Arrow) -> Arrow. %% %% compose function(s) using AST notation %% %% f(_) . g(_) -> case f(_) of undefined -> undefined ; X -> g(X) end %% '.'(_, {option, VarX, G}, {call, Ln, Ff0, Fa0}) -> VarN = datum_cat:uuid(), Expr = dot_expr(Ln, VarX, {call, Ln, Ff0, datum_cat:cc_bind_var({var, Ln, VarN}, Fa0)}, G), {option, VarN, Expr}; '.'(_, {option, VarX, G}, {generate, Ln, {var, _, VarN}, F}) -> Expr = dot_expr(Ln, VarN, datum_cat:cc_bind_var({var, Ln, VarX}, F), G), {option, VarX, Expr}; '.'(Cat, {call, Ln, Ff0, Fa0}, G) -> VarN = datum_cat:uuid(), Expr = {call, Ln, Ff0, datum_cat:cc_bind_var({var, Ln, VarN}, Fa0)}, '.'(Cat, {option, VarN, Expr}, G); '.'(Cat, {generate, _Ln, _Var, F}, G) -> %% ignore tail arrow '.'(Cat, F, G). %% %% dot_expr(Ln, VarX, F, G) -> {'case', Ln, F, [ {clause, Ln, [{atom, Ln, undefined}], [], [{atom, Ln, undefined}] }, {clause, Ln, [{var, Ln, VarX}], [], [G] } ]}. %% %% return dot-composition chain. chain({option, _, Expr}) -> Expr. %% %% curry do-composition chain into partial application curry({option, VarX, {'case', Ln, _, _} = Expr}) -> {'fun', Ln, {clauses, [ {clause, Ln, [{var, Ln, VarX}], [], [Expr] } ]} }. %%%------------------------------------------------------------------ %%% %%% transformers %%% %%%------------------------------------------------------------------ %% %% lifts a value to object of category -spec unit(_) -> datum:option(_). unit(X) -> X. %% %% lifts a failure to error object of category -spec fail(_) -> datum:option(_). fail(_) -> undefined. %% %% conditionally lifts a value to object or error of category -spec require(boolean(), _, _) -> datum:option(). require(true, X, _) -> X; require(false, _, _) -> undefined. %% %% transforms sequence of objects into object of category. -spec sequence([datum:option(_)]) -> datum:option([_]). sequence([undefined | _]) -> undefined; sequence([Head | Seq]) -> case sequence(Seq) of undefined -> undefined; Tail -> [Head | Tail] end; sequence([]) -> []. %% %% transforms nested objects into object of category -spec flatten(_) -> _. flatten(X) -> X. %% %% transforms option category to identity -spec optionT( datum:option() ) -> datum:option(_). optionT(X) -> X. %% %% -spec eitherT( datum:either(_) ) -> datum:option(_). eitherT({ok, X}) -> X; eitherT({error, _}) -> undefined.