[erlang-questions] Nested Case Statements v.s. multiple functions

Richard A. O'Keefe ok@REDACTED
Wed Sep 27 07:26:40 CEST 2017



On 26/09/17 3:40 PM, dploop@REDACTED wrote:
> This problem also bothered me for a long time, so I write a parse
> transform to deal with it. To be specific, say we want to write a
> function which takes two integers and returns  the product and quotient
> of them.
>
> muldiv(First, Second) ->
>     case is_integer(First) of
>         true ->
>             case is_integer(Second) of
>                 true ->
>                     Product = First * Second,
>                     case Second =/= 0 of
>                         true ->
>                             Quotient = First div Second,
>                             {ok, {Product, Quotient}};
>                         false ->
>                             {error, "Second must not be zero!"}
>                     end;
>                 false ->
>                     {error, "Second must be an integer!"}
>             end;
>         false ->
>             {error, "First must be an integer!"}
>     end.

-module(must_be).
-export([integer/2, nonzero/2, ...]).

integer(X, _Msg) when is_integer(X) ->
     X;
integer(X, Msg) ->
     throw({must_be,integer,X,Msg}).

nonzero(X, _Msg) when X == 0 ->
     X;
nonzero(X, Msg) ->
     throw({must_be,nonzero,X,Msg}).

-module(yourapp).

muldiv(First, Second) ->
     try
         must_be:integer(First, <<"first">>),
         must_be:integer(Second, <<"second">>),
         must_be:nonzero(Second, <<"second">>),
         {ok, {First*Second, First div Second}
     catch
         throw:Reason -> {error, Reason}
     end.

No parse transform needed.
>
>
> Ugly, at least I think this "rocket" is not beautiful. Even worse, if
> requirement changed, you should modify this code on a large scale. To
> avoid this problem, you can just rewrite your code like this
>
> muldiv(First, Second) ->
>     do@([esugar_do_transform_error ||
>         case is_integer(First) of
>         true -> return(next);
>         false -> fail("First must be an integer!")
>         end,
>         case is_integer(Second) of
>         true -> return(next);
>         false -> fail("Second must be an integer!")
>         end,
>         Product = First * Second,
>         case Second =/= 0 of
>         true -> return(next);
>         false -> fail("Second must not be zero!")
>         end,
>         Quotient = First div Second,
>         return({Product, Quotient})
>     ]).

To me, this is worse than what you started with.
At least the starting version was completely standard Erlang
syntax, so I could figure it out.  Here, amongst other things,
I am flummoxed by 'return'.

For internal use, I'd rather follow the "let it crash"
approach and write

muldiv(First, Second)
   when is_integer(First), is_integer(Second), Second =/= 0 ->
     {First * Second, First div Second}.

Amongst other things, when First is *not* an integer, it is
at least as useful to know what it *is* as that it is called
First.

I do appreciate that there are cases where that can't be done.
(The must_be functions come from Quintus Prolog.  When error-
handling was introduced, it was necessary to trawl through all
the system sources making sure that errors were properly
reported *and classified* correctly, so must_be_integer
reported a 'type' error and must_be_nonzero reported a
'domain' error.  The reason for the distinction was that type
errors generally indicated that you had passed the wrong
argument, while domain errors generally indicated you had the
right argument but its value was no good for some other reason.)





More information about the erlang-questions mailing list