Implement hope_result:lift_map_exn/3
authorSiraaj Khandkar <siraaj@khandkar.net>
Tue, 24 Mar 2015 17:57:23 +0000 (13:57 -0400)
committerSiraaj Khandkar <siraaj@khandkar.net>
Tue, 24 Mar 2015 17:57:23 +0000 (13:57 -0400)
as base implementation of other lift_exn functions.

src/hope_fun.erl [new file with mode: 0644]
src/hope_result.erl
test/hope_fun_SUITE.erl [new file with mode: 0644]
test/hope_result_SUITE.erl

diff --git a/src/hope_fun.erl b/src/hope_fun.erl
new file mode 100644 (file)
index 0000000..61babe9
--- /dev/null
@@ -0,0 +1,10 @@
+-module(hope_fun).
+
+-export(
+    [ id/1
+    ]).
+
+-spec id(A) ->
+    A.
+id(X) ->
+    X.
index dec6e79..930f2e0 100644 (file)
@@ -4,6 +4,8 @@
 
 -export_type(
     [ t/2
+    , exn_class/0
+    , exn_value/1
     ]).
 
 -export(
     , pipe/2
     , lift_exn/1
     , lift_exn/2
+    , lift_map_exn/3
     ]).
 
+-type exn_class() ::
+      error
+    | exit
+    | throw
+    .
+
+-type exn_value(A) ::
+    {exn_class(), A}.
 
 -type t(A, B) ::
       {ok, A}
@@ -65,33 +76,40 @@ pipe([F|Fs], X) ->
     end.
 
 -spec lift_exn(F) -> G
-    when F     :: fun((A)-> B)
-       , G     :: fun((A)-> t(B, {Class, Reason :: any()}))
-       , Class :: error
-                | exit
-                | throw
+    when F     :: fun((A) -> B)
+       , G     :: fun((A) -> t(B, exn_value(any())))
        .
 lift_exn(F) when is_function(F, 1) ->
-    fun(X) ->
-        try
-            {ok, F(X)}
-        catch Class:Reason ->
-            {error, {Class, Reason}}
-        end
-    end.
+    ID = fun hope_fun:id/1,
+    lift_map_exn(F, ID, ID).
+
+-spec lift_exn(F, ErrorTag) -> G
+    when F :: fun((A) -> B)
+       , G :: fun((A) -> t(B, {ErrorTag, exn_value(any())}))
+       .
+lift_exn(F, ErrorTag) when is_function(F, 1) ->
+    ID = fun hope_fun:id/1,
+    Tag = fun (Reason) -> {ErrorTag, Reason} end,
+    lift_map_exn(F, ID, Tag).
 
--spec lift_exn(F, Label) -> G
-    when F     :: fun((A)-> B)
-       , G     :: fun((A)-> t(B, {Label, {Class, Reason :: any()}}))
-       , Class :: error
-                | exit
-                | throw
+-spec lift_map_exn(F, MapOk, MapError) -> G
+    when F        :: fun((A) -> B)
+       , MapOk    :: fun((B) -> C)
+       , MapError :: fun((exn_value(any())) -> Error)
+       , G        :: fun((A) -> t(C, Error))
        .
-lift_exn(F, Label) when is_function(F, 1) ->
+lift_map_exn(F, MapOk, MapError) when is_function(F, 1) ->
     fun(X) ->
-        try
-            {ok, F(X)}
-        catch Class:Reason ->
-            tag_error({error, {Class, Reason}}, Label)
+        Result =
+            try
+                {ok, F(X)}
+            catch Class:Reason ->
+                {error, {Class, Reason}}
+            end,
+        % Applying maps separately as to not unintentionally catch an exception
+        % raised in a map.
+        case Result
+        of  {ok   , _}=Ok    -> map      (Ok   , MapOk)
+        ;   {error, _}=Error -> map_error(Error, MapError)
         end
     end.
diff --git a/test/hope_fun_SUITE.erl b/test/hope_fun_SUITE.erl
new file mode 100644 (file)
index 0000000..d4e2281
--- /dev/null
@@ -0,0 +1,41 @@
+-module(hope_fun_SUITE).
+
+%% Callbacks
+-export(
+    [ all/0
+    , groups/0
+    ]).
+
+%% Test cases
+-export(
+    [ t_id/1
+    ]).
+
+
+-define(GROUP, hope_fun).
+
+
+%% ============================================================================
+%% Common Test callbacks
+%% ============================================================================
+
+all() ->
+    [ {group, ?GROUP}
+    ].
+
+groups() ->
+    Tests =
+        [ t_id
+        ],
+    Properties = [parallel],
+    [ {?GROUP, Properties, Tests}
+    ].
+
+
+%% =============================================================================
+%%  Test cases
+%% =============================================================================
+
+t_id(_Cfg) ->
+    X = foo,
+    X = hope_fun:id(X).
index d323cee..ab31503 100644 (file)
@@ -14,6 +14,7 @@
     , t_pipe_error/1
     , t_hope_result_specs/1
     , t_lift_exn/1
+    , t_lift_map_exn/1
     , t_return/1
     , t_map/1
     , t_map_error/1
@@ -48,6 +49,7 @@ groups() ->
         ],
     LiftTests =
         [ t_lift_exn
+        , t_lift_map_exn
         ],
     OtherTests =
         [ t_return
@@ -101,6 +103,32 @@ t_lift_exn(_Cfg) ->
     {error, {Class, Reason}} = G(ok),
     {error, {Label, {Class, Reason}}} = H(ok).
 
+t_lift_map_exn(_Cfg) ->
+    FOk  = fun ({}) -> foo end,
+    FExn = fun ({}) -> throw(baz) end,
+    MapOk          = fun (foo)          -> bar end,
+    MapOkThrows    = fun (foo)          -> throw(exn_from_ok_map) end,
+    MapError       = fun ({throw, baz}) -> qux end,
+    MapErrorThrows = fun ({throw, baz}) -> throw(exn_from_error_map) end,
+    GOk          = hope_result:lift_map_exn(FOk , MapOk      , MapError),
+    GOkThrows    = hope_result:lift_map_exn(FOk , MapOkThrows, MapError),
+    GError       = hope_result:lift_map_exn(FExn, MapOk      , MapError),
+    GErrorThrows = hope_result:lift_map_exn(FExn, MapOk      , MapErrorThrows),
+    {ok, bar} = GOk({}),
+    {error, qux} = GError({}),
+    ok =
+        try
+            must_not_return = GOkThrows({})
+        catch throw:exn_from_ok_map ->
+                ok
+        end,
+    ok =
+        try
+            must_not_return = GErrorThrows({})
+        catch throw:exn_from_error_map ->
+                ok
+        end.
+
 t_return(_Cfg) ->
     X = foo,
     {ok, X} = hope_result:return(X).
This page took 0.023753 seconds and 4 git commands to generate.