moving general-purpose functions from httpd_util to stdlib

classic Classic list List threaded Threaded
7 messages Options
Reply | Threaded
Open this post in threaded view
|

moving general-purpose functions from httpd_util to stdlib

Bengt Kleberg-3

> Date: Mon, 14 Apr 2003 00:01:55 -0500
> From: Chris Pressey <cpressey>
...deleted

> This brings up one of those burning questions of our times - that is,
> what exactly "defensive programming" means in the context of Erlang.
>
> Say you have a function foo() that can return either an integer or the
> atom 'error'.

an alternative to this problem is described by Richard Carlsson:

                - A function should *not* return wrapped values like
                  {ok,Value}/{error,Reason} to indicate success or
                  failure. The assumed behaviour should be success,
                  and failures should be signalled by exceptions,
                  as described below.


bengt



Reply | Threaded
Open this post in threaded view
|

moving general-purpose functions from httpd_util to stdlib

Ulf Wiger-4
On Mon, 14 Apr 2003, Bengt Kleberg wrote:

>> Date: Mon, 14 Apr 2003 00:01:55 -0500
>> From: Chris Pressey <cpressey>
>...deleted
>
>> This brings up one of those burning questions of our times - that is,
>> what exactly "defensive programming" means in the context of Erlang.
>>
>> Say you have a function foo() that can return either an integer or the
>> atom 'error'.
>
>an alternative to this problem is described by Richard Carlsson:
>
> - A function should *not* return wrapped values like
>  {ok,Value}/{error,Reason} to indicate success or
>  failure. The assumed behaviour should be success,
>  and failures should be signalled by exceptions,
>  as described below.

This is the behaviour I much prefer.

I think one should always try to think about what a user can
reasonably do if the requested operation doesn't succeed. If
the normal case is to exit, then I think it's best to have
the original function exit right away. If the odd caller
does not want to exit, there's always catch.

I've seen tons of code where the caller checks the return
value in the following manner:

case foo(X,Y,Z) of
   {ok, Result} ->
      ...;
   {error, Reason} ->
      {error, Reason}
end.

If this type of pattern is scattered all over in your
programs, then you'd be much better off writing functions
that either succeed or fail distinctly.

One of my favourite examples has been timer:send_after/3,
which returns {ok, TRef} | {error, Reason}. How many times
when you've ordered a timer has there been any reasonable
alternative action in the event that a timer weren't
available?


/Uffe
--
Ulf Wiger, Senior Specialist,
   / / /   Architecture & Design of Carrier-Class Software
  / / /    Strategic Product & System Management
 / / /     Ericsson AB, Connectivity and Control Nodes



Reply | Threaded
Open this post in threaded view
|

moving general-purpose functions from httpd_util to stdlib

Luke Gorrie-3
Ulf Wiger <etxuwig> writes:

> >an alternative to this problem is described by Richard Carlsson:
> >
> > - A function should *not* return wrapped values like
> >  {ok,Value}/{error,Reason} to indicate success or
> >  failure. The assumed behaviour should be success,
> >  and failures should be signalled by exceptions,
> >  as described below.
>
> This is the behaviour I much prefer.
>
> I think one should always try to think about what a user can
> reasonably do if the requested operation doesn't succeed. If
> the normal case is to exit, then I think it's best to have
> the original function exit right away. If the odd caller
> does not want to exit, there's always catch.

I agree, except that I'm not comfortable enough with using 'catch',
since it always catches everything and mucks up your backtraces. I
would like to have more explicit control over what I actually catch.

The proposed 'try-catch' construct could be nice for this.

Cheers,
Luke



Reply | Threaded
Open this post in threaded view
|

moving general-purpose functions from httpd_util to stdlib

Robert Virding-5
In reply to this post by Bengt Kleberg-3
----- Original Message -----
From: "Bengt Kleberg" <eleberg>
To: <erlang-questions>
Sent: Monday, April 14, 2003 8:08 AM
Subject: Re: moving general-purpose functions from httpd_util to stdlib


>
> > Date: Mon, 14 Apr 2003 00:01:55 -0500
> > From: Chris Pressey <cpressey>
> ...deleted
>
> > This brings up one of those burning questions of our times - that is,
> > what exactly "defensive programming" means in the context of Erlang.
> >
> > Say you have a function foo() that can return either an integer or the
> > atom 'error'.
>
> an alternative to this problem is described by Richard Carlsson:
>
> - A function should *not* return wrapped values like
>   {ok,Value}/{error,Reason} to indicate success or
>   failure. The assumed behaviour should be success,
>   and failures should be signalled by exceptions,
>   as described below.

I think I would try to qualify that a little more. It depends on what type
of failure you mean. I would generally say that bad argument type
or values should generate an exit so it is quite clear that Chris's
hex conversion functions should only return the value and exit on
failure. However, there are many cases where the non-success
case does not really represent an error and then it should not
generate an exit but use qualified return values like
{ok,V}/{error,R}. Maybe the convention to use ok/error was badly
chosen and we should have used yes/no but I think it would be
worse to try and have two, or more, "conventions". Better to
have one and try to explain how it is used.

This is why most of the original, older libraries at least very rarely
check arguments types and values but just assume they are
correct and otherwise exit. As do most bifs. You can't do anything
sensible if the arguments are all wrong.

Another example would be file:open. As I envisaged when I wrote
it was that if the arguments were of the wrong type or had illegal
values ('creat') then it should exit. However, not being able to
open the file I didn't really see as an error so it returned qualified
values to indicate this. What it exactly does now I don't know.

Not everyone agreed with me.

Basically there are errors and errors.

Robert



Reply | Threaded
Open this post in threaded view
|

moving general-purpose functions from httpd_util to stdlib

Chris Pressey
Thanks, everybody.

But I have to apologize for that bad example.  The 'error' is a red
herring that clouds the issue.  The question is more about whether it is
wiser, in general, to write "open" or "closed" cases.

A (possibly) better example:

  % "open" case
  case A > B of
    true ->
      41;
    _ ->
      42
  end

  % "closed" case
  case A > B of
    true ->
      41;
    false ->
      42
  end

  % "let-it-crash" style - seems almost like a different problem
  case catch begin
                true = A > B,
                41
             end of
    {'EXIT', Reason} ->
      42;
    Result ->
      Result
  end

-Chris

On Tue, 15 Apr 2003 02:12:39 +0200
"Robert Virding" <robert.virding> wrote:

> ----- Original Message -----
> From: "Bengt Kleberg" <eleberg>
> To: <erlang-questions>
> Sent: Monday, April 14, 2003 8:08 AM
> Subject: Re: moving general-purpose functions from httpd_util to
> stdlib
>
>
> >
> > > Date: Mon, 14 Apr 2003 00:01:55 -0500
> > > From: Chris Pressey <cpressey>
> > ...deleted
> >
> > > This brings up one of those burning questions of our times - that
> > > is, what exactly "defensive programming" means in the context of
> > > Erlang.
> > >
> > > Say you have a function foo() that can return either an integer or
> > > the atom 'error'.
> >
> > an alternative to this problem is described by Richard Carlsson:
> >
> > - A function should *not* return wrapped values like
> >   {ok,Value}/{error,Reason} to indicate success or
> >   failure. The assumed behaviour should be success,
> >   and failures should be signalled by exceptions,
> >   as described below.
>
> I think I would try to qualify that a little more. It depends on what
> type of failure you mean. I would generally say that bad argument type
> or values should generate an exit so it is quite clear that Chris's
> hex conversion functions should only return the value and exit on
> failure. However, there are many cases where the non-success
> case does not really represent an error and then it should not
> generate an exit but use qualified return values like
> {ok,V}/{error,R}. Maybe the convention to use ok/error was badly
> chosen and we should have used yes/no but I think it would be
> worse to try and have two, or more, "conventions". Better to
> have one and try to explain how it is used.
>
> This is why most of the original, older libraries at least very rarely
> check arguments types and values but just assume they are
> correct and otherwise exit. As do most bifs. You can't do anything
> sensible if the arguments are all wrong.
>
> Another example would be file:open. As I envisaged when I wrote
> it was that if the arguments were of the wrong type or had illegal
> values ('creat') then it should exit. However, not being able to
> open the file I didn't really see as an error so it returned qualified
> values to indicate this. What it exactly does now I don't know.
>
> Not everyone agreed with me.
>
> Basically there are errors and errors.
>
> Robert
>


Reply | Threaded
Open this post in threaded view
|

moving general-purpose functions from httpd_util to stdlib

Robert Virding-5
Definitely the closed case. The extra cost is negligible and the meaning
much clearer, especially if the test is not so obviously a "safe" boolean.

Robert

----- Original Message -----
From: "Chris Pressey" <cpressey>
To: "Robert Virding" <robert.virding>
Cc: <eleberg>; <erlang-questions>
Sent: Tuesday, April 15, 2003 8:06 PM
Subject: Re: moving general-purpose functions from httpd_util to stdlib


> Thanks, everybody.
>
> But I have to apologize for that bad example.  The 'error' is a red
> herring that clouds the issue.  The question is more about whether it is
> wiser, in general, to write "open" or "closed" cases.
>
> A (possibly) better example:
>
>   % "open" case
>   case A > B of
>     true ->
>       41;
>     _ ->
>       42
>   end
>
>   % "closed" case
>   case A > B of
>     true ->
>       41;
>     false ->
>       42
>   end
>
>   % "let-it-crash" style - seems almost like a different problem
>   case catch begin
>                 true = A > B,
>                 41
>              end of
>     {'EXIT', Reason} ->
>       42;
>     Result ->
>       Result
>   end
>
> -Chris



Reply | Threaded
Open this post in threaded view
|

"open" vs "closed" cases

Chris Pressey
On Wed, 16 Apr 2003 23:29:02 +0200
"Robert Virding" <robert.virding> wrote:

> Definitely the closed case. The extra cost is negligible and the
> meaning much clearer, especially if the test is not so obviously a
> "safe" boolean.
>
> Robert

OK, I've been looking at the way I write code, and I often use "open"
cases - partly because I communicate at the user level (or a level where
I want to assume certain defaults if input is missing or incorrect), and
partly because I'm lazy and I know it's a bad habit but I do it anyway.

As penance, I *have* been trying to write stricter interfaces & cases
where appropriate lately, for example in the new API for my webserver, I
have the following interface (roughly) for modules:

  start(conf()) -> conf()

  serve(conf()) -> {not_found, conf()} | {{serve, data()}, conf()}

In the first case, crashing if something goes wrong is good; there's
nothing sensible that can be done after a module can't be started.

In the second case, the error is not really a 'show-stopper' type error
- it's more like a condition, so I pass it back as a value.
(However, if the serve function does crash, the webserver does generate
a 501 internal server error response - which also makes sense.)

So, yes - I agree (with Robert) that errors come in a spectrum.  I also
agree (with Ulf) that it's usually best to write only for the correct
case and let everything else crash - and I hope that a future version of
Erlang will incorporate 'try', to make that option even more feasible.

-Chris