+
+-spec lift_exn(F) -> G
+ when F :: fun((A) -> B)
+ , G :: fun((A) -> t(B, exn_value(any())))
+ .
+lift_exn(F) when is_function(F, 1) ->
+ 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_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_map_exn(F, MapOk, MapError) when is_function(F, 1) ->
+ fun(X) ->
+ 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.