[eeps] EEP 49: Value-Based Error Handling Mechanisms

Martin Bjorklund mbj@REDACTED
Wed Dec 5 08:50:45 CET 2018


Hi,

Michael Truog <mjtruog@REDACTED> wrote:
> I know this is a bit after most comments have occurred, but it seems
> ok since a decision hasn't been made yet.  I like the change to the
> begin/end, even though it attaches some magic interpretation of an ok
> 2-tuple and an error 2-tuple.

I don't like it, for that particular reason.

  Trying to make the begin/end change
> more complex to handle other return values for Success/Error
> conditions (perhaps by adding type specifications to the syntax)
> doesn't seem like a good direction

How about something like:

  begin
      {ok, A} =~ do_something(),
      {true, B} =~ do_something_else(A),
      ok =~ at_last(B)
  end

where =~ means that if the RHS value doesn't match the LHS expression,
the RHS value is returned from the begin ... end expression.

So if do_something_else() above returns false, the begin ... end would
return false.


But even with this, I'm not sure the benefits are worth the added
complexity of the language.


/martin

> , so the begin/end change seems good
> as-is.  The begin/end change in EEP 49 should also make the approach
> more efficient than the equivalent source code using functions,
> similar to what is described at
> https://github.com/erlang/eep/blob/master/eeps/eep-0049.md#obsoleting-messy-patterns
> because less data is required to accomplish the same control flow.
> 
> If the begin/end change in EEP 49 is not acceptable for some reason,
> or if people prefer an approach with functions, I have two functions
> which I have found useful to avoid "the pointy-case statement" problem
> that EEP 49 addresses.  They are eval/1 and accum/2, as shown below:
> 
> -type state() :: any().
> -type eval_value() :: any().
> 
> -spec accum(L :: list({any(),
>                        fun((any(), state()) ->
>                            {ok, state()} | {{error, any()},
> state()})}),
>             State :: state()) ->
>     {ok, state()} | {{error, any()}, state()}.
> 
> accum([], State) ->
>     {ok, State};
> accum([{Value, F} | L], State) ->
>     case F(Value, State) of
>         {ok, StateNew} ->
>             accum(L, StateNew);
>         {{error, _}, _} = Error ->
>             Error
>     end.
> 
> -spec eval(L :: list({eval_value(),
>                       fun((eval_value()) -> {ok, any()} | {error,
> any()})})) ->
>     tuple().
> 
> eval(L) ->
>     eval(L, []).
> 
> eval([], Output) ->
>     erlang:list_to_tuple([ok | lists:reverse(Output)]);
> eval([{Value, F} | L], Output) ->
>     case F(Value) of
>         {ok, ValueNew} ->
>             eval(L, [ValueNew | Output]);
>         {error, _} = Error ->
>             Error
>     end.
> 
> These functions may be helpful in Erlang/OTP, in the absence of the
> EEP 49 begin/end change.  With the EEP 49 begin/end change, they may
> help with backwards compatible source code but there doesn't seem like
> there should be any type-checking advantage (type-checking should be
> worse this way) or any advantage related to avoiding exceptions (i.e.,
> avoiding the need to catch exceptions that causes the function
> call-path to be impure).  Both use a list of 2-tuples where the
> 2-tuple represents Value -> Function.  The eval Function is arity 1
> and a success result is an ok tuple one larger than the size of the
> list (allowing a match on all results).  The accum Function is arity 2
> to allow the accumulation of state data in the second argument, with
> the final state value getting returned in an ok 2-tuple with a
> success.
> 
> These functions could easily be added to the Erlang/OTP lists module
> to try and guide people away from creating pointy-case statement
> structures in their functions (causing the columns in the source code
> to grow to become less readable and possibly causing more functions to
> be created than necessary to break-up the functionality).  For
> handling functions that require more arguments, a tuple or list could
> be passed as the Value provided (similar to what is commonly done with
> init/1 functions used with OTP behaviours).
> 
> Some use of these functions can be seen at:
> https://github.com/CloudI/CloudI/blob/ce54b4ae354b5633593fba6bf68f4eedfab7985c/src/lib/cloudi_core/src/cloudi_core_i_logger.erl#L813-L826
> https://github.com/CloudI/CloudI/blob/develop/src/lib/cloudi_core/src/cloudi_core_i_configuration.erl#L3299-L3319
> 
> So, while I like the begin/end EEP 49 change, the accum/2 and eval/1
> functions described above may help the discussion further, as an
> alternative or additional change.
> 
> Best Regards,
> Michael
> 
> _______________________________________________
> eeps mailing list
> eeps@REDACTED
> http://erlang.org/mailman/listinfo/eeps



More information about the eeps mailing list