How to handle nested case

Michael Truog mjtruog@REDACTED
Mon Sep 20 05:01:50 CEST 2021


On 9/19/21 7:22 PM, Richard O'Keefe wrote:
> It seems extremely likely that the code is not referentially
> transparent to begin with.
> That was meant as an *example* of try/catch, and the question of ambiguity was
> addressed in that message of mine and in someone else's.
>
> My *actual* recommendation is the higher-order combinator ok(f(), fun
> (R) -> ... end), which I like precisely because it is
> straightforwardly functional and adaptable.

Your approach with continuation-passing style anonymous functions and 
explicit matches are good if "let it crash" is possible. However, often 
error information needs to be returned as a value (e.g., an error with 
input that is not an Erlang process failure). My view of the problem was 
input validation while the example source code looks like it is related 
to the validity of the static configuration provided.  If the errors in 
the example are static system configuration, then it is best to have the 
fail-fast approach in your examples.

Best Regards,
Michael

> On Mon, 20 Sept 2021 at 14:05, Michael Truog <mjtruog@REDACTED> wrote:
>> If (try) catch is used that means the source code will not be
>> referentially transparent (it will be impure) because any of the three
>> functions may also throw.  That makes the approach an anti-pattern,
>> something to avoid to promote code that easy to inline, optimize, test
>> and understand.
>>
>>
>> On 9/19/21 5:32 PM, Richard O'Keefe wrote:
>>> What is wrong with 'try'?
>>>
>>> ok({ok,R}) -> R;
>>> ok(E) -> throw(E).
>>>
>>>      try
>>>         Server = ok(get_radius_host()),
>>>         Port = ok(get_radius_port()),
>>>         Secret = ok(get_radius_secret),
>>>         {ok,{Server,Port,Secret}}
>>>      catch throw:E ->
>>>         E
>>>      end
>>>
>>> WARNING: UNTESTED CODE.
>>>
>>> I'd be inclined to do something slightly different:
>>>
>>> ok({ok,R}, F) -> F(R);
>>> ok(E) -> E.
>>>
>>>      ok(get_radius_server(), fun (Server) ->
>>>      ok(get_radius_host(),    fun (Port) ->
>>>      ok(get_radius_secret(), fun (Secret) ->
>>>         {ok,{Server,Port,Secret}}
>>>      end) end) end)
>>>
>>> (which is rather like using >>= \x -> rather than 'do' in Haskell).
>>>
>>> I would also be wondering whether it is possible for a non-OK result to be
>>> ambiguous.  Are there values for E that could have come from more than
>>> one of these 'get_*' functions?  Might the caller of your code care about
>>> where the error came from?
>>>
>>> I find myself wondering whether a simple
>>>      {ok,Server} = ...
>>>      {ok,Port} = ....
>>>      {ok,Secret} = ...
>>> might not be the most idiomatic "let it crash" Erlang way of all.
>>>
>>> There is some important context missing here.
>>> * Why isn't "let it crash" the right answer here?
>>> * Why should the caller be told that an error occurred but
>>>      in a possibly ambiguous way?
>>> * Is there no other validation to be done?
>>> * What do the surrounding comments say?
>>>
>>> On Sun, 19 Sept 2021 at 08:50, Gian Lorenzo Meocci <glmeocci@REDACTED> wrote:
>>>> Hi,
>>>> I have a function like this:
>>>>
>>>> get_nas_from_conf() ->
>>>> case get_radius_host() of
>>>> {ok, Server} ->
>>>> case get_radius_port() of
>>>> {ok, Port} ->
>>>> case get_radius_secret() of
>>>> {ok, Secret} ->
>>>> {Server, Port, Secret};
>>>> E -> E
>>>> end;
>>>> E ->
>>>> E
>>>> end;
>>>> E ->
>>>> E
>>>> end.
>>>>
>>>> Which is the best way to write this kind of function?
>>>>
>>>> I'd like to have a with operator like in Elixir, to rewrite my function in this way:
>>>>
>>>> get_nas_from_conf() ->
>>>> with {ok, Server} <- get_radius_host(),
>>>> {ok, Port} = get_radius_port(),
>>>> {ok, Secret} = get_radius_secret() ->
>>>> {Server, Port, Secret};
>>>> catch
>>>> {error, _Reason} = E ->
>>>> E
>>>> end.
>>>>
>>>> Any suggestion?
>>>>
>>>> --
>>>> GL
>>>> https://www.meocci.it



More information about the erlang-questions mailing list