Define explicit map_ok as implementation of map
[hope.git] / src / hope_result.erl
index 7cae0a3..0f9fb05 100644 (file)
@@ -4,16 +4,30 @@
 
 -export_type(
     [ t/2
+    , exn_class/0
+    , exn_value/1
     ]).
 
 -export(
     [ return/1
-    , map/2
+    , map/2        % map/2 is alias for map_ok/2
+    , map_ok/2
+    , map_error/2
+    , tag_error/2
     , 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}
@@ -28,11 +42,30 @@ return(X) ->
 
 -spec map(t(A, Error), fun((A) -> (B))) ->
     t(B, Error).
-map({ok, X}, F) ->
+map({_, _}=T, F) ->
+    map_ok(T, F).
+
+-spec map_ok(t(A, Error), fun((A) -> (B))) ->
+    t(B, Error).
+map_ok({ok, X}, F) ->
     {ok, F(X)};
-map({error, _}=Error, _) ->
+map_ok({error, _}=Error, _) ->
     Error.
 
+-spec map_error(t(A, B), fun((B) -> (C))) ->
+    t(A, C).
+map_error({ok, _}=Ok, _) ->
+    Ok;
+map_error({error, Reason}, F) ->
+    {error, F(Reason)}.
+
+-spec tag_error(t(A, Reason), Tag) ->
+    t(A, {Tag, Reason}).
+tag_error({ok, _}=Ok, _) ->
+    Ok;
+tag_error({error, Reason}, Tag) ->
+    {error, {Tag, Reason}}.
+
 -spec pipe([F], X) ->
     t(Ok, Error)
     when X     :: any()
@@ -49,33 +82,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 ->
-            {error, {Label, {Class, Reason}}}
+        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   (Ok   , MapOk)
+        ;   {error, _}=Error -> map_error(Error, MapError)
         end
     end.
This page took 0.020932 seconds and 4 git commands to generate.