Safety strategies

2011-02-06 Thread bearophile
This is one of the most interesting CS ideas I've found in the past month, here 
I have started to understand rationally why dynamic languages like Python may 
generally be not significantly more bug prone than tightly statically typed 
languages like ML or D:
http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.Darticle_id=126394

Recently in this Reddit thread I have found a third interesting safety strategy:
http://www.reddit.com/r/programming/comments/fdrtz/lisp_atoms_ruby_symbols_erlang_atoms_how_useful/

It's explained more in focus here:
http://stackoverflow.com/questions/3399956/pattern-matching-f-vs-erlang

Erlang software systems are among the most reliable ones, yet it refuses the 
static safety used by ML-class languages (like SML, Haskell, ATS, OCaML, F# and 
many more). It's really a different strategy to create reliable systems. 

--

Some mixed quotations from that stackoverflow and reddit discussions:

When you run into a constraint that needs to change you change the types. If 
there is a case I didn't consider I add a new value to a sum type or enum or 
class or whatever. In a pattern matching language this will break things but 
the compiler will handily point out exactly which patterns no longer make any 
sense or which ones are now non-exhaustive. Fixing it straight forward.


You mention the value of the compiler pointing out non-exhaustive pattern 
matching. It might interest you to know that in Erlang that is a no-no. Your 
pattern matching should not handle cases you do not know how to deal with in a 
let it crash manner. Actually Joe Armstrong (Erlang's creator) puts it as let 
some other process fix the error. The idea is not unlike the idea common in 
exception handling that you shouldn't catch all exceptions but rather those 
that you can handle.


Crash here doesn't mean bring down the program. In Erlang, a program typically 
consists of a number of processes, interacting via message passing. If a 
process crashes then another process handles that event (oversimplified). It 
may elect to respawn the process crashed and/or deal with the error in whatever 
way it sees fit. It may also simply die as well, and then its supervisor 
handles the result. As I said, the nearest thing to this is other languages is 
exception handling.

The whole point, as it is the point in exception handling, is that the process 
or function should not be programmed that defensively. Let's have an example. 
The next is F# code:

let testColor c =
match c with
| Black - 1
| White - 0
| Other - failwith unexpected color

an almost equivalent Erlang code is

case c of
black - 1;
white - 0
end;

In F#, I must use the failwith function. The reason is that the language forces 
me to have exhaustive pattern matching. In Erlang it doesn't. In fact, Erlang 
encourages me not to fill in a handler for all other cases where the color is 
not black or white.

Now the results:
- In F#, I know that my function has handled all cases. I can depend on that. 
On the other hand, because I was forced to do what is essentially error 
handling as early as possible I might get it wrong. In the example, I returned 
a generic error unexpected color which may or may not be true (in this case 
copied from here I am using an active pattern and the error might be anything 
else in the pattern function). In short, I am losing information because I had 
to handle the potential error early. This is the same argument in exception 
handling. You don't catch an exception which you do not know how to handle; you 
let it go up the stack.
- In Erlang, if a colour is not matched then the pattern matching raises a 
badmatch error. This can be handled at the appropriate level in the function 
stack or the process hierarchy.
- On the other hand, the F# approach is probably more amenable to proof of 
correctness. Depending on whether you think this is an important enough issue, 
you might prefer that.

I have worked with both. It really depends on the whole approach you are using. 
Erlang has a very impressive pattern matching and it is non-exhaustive, because 
the whole model of fault handling is different from the ML approach where a 
fault is a world collapses type of event. Armstrong's PhD thesis was Making 
reliable distributed systems in the presence of software errors. The ML 
approach is to not have software errors.

The point of the Erlang philosophy is not to produce bad code that always 
crashes. Let it crash means let some other process fix the error. Instead of 
writing the function so that it can handle all possible cases, let the caller 
(for example) handle the bad cases which are thrown automatically. For those 
with Java background, it is like the difference between having a language with 
checked exceptions which must declare everything it will possibly return with 
every possible exception, and having a language in which functions may raise 
exceptions that are not 

Re: Safety strategies

2011-02-06 Thread Ulrik Mikaelsson
Interesting idea, especially when you start considering how software
might survive errors in dependent systems, I.E. hardware, networking,
or software components outside the control of the compiler.

It sounds like the Erlang approach is far more likely to survive
spurious bitflips, power-fluctuations and others things we all prefer
to pretend don't exist.

2011/2/6 bearophile bearophileh...@lycos.com:
 This is one of the most interesting CS ideas I've found in the past month, 
 here I have started to understand rationally why dynamic languages like 
 Python may generally be not significantly more bug prone than tightly 
 statically typed languages like ML or D:
 http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.Darticle_id=126394

 Recently in this Reddit thread I have found a third interesting safety 
 strategy:
 http://www.reddit.com/r/programming/comments/fdrtz/lisp_atoms_ruby_symbols_erlang_atoms_how_useful/

 It's explained more in focus here:
 http://stackoverflow.com/questions/3399956/pattern-matching-f-vs-erlang

 Erlang software systems are among the most reliable ones, yet it refuses the 
 static safety used by ML-class languages (like SML, Haskell, ATS, OCaML, F# 
 and many more). It's really a different strategy to create reliable systems.

 --

 Some mixed quotations from that stackoverflow and reddit discussions:

 When you run into a constraint that needs to change you change the types. If 
 there is a case I didn't consider I add a new value to a sum type or enum or 
 class or whatever. In a pattern matching language this will break things but 
 the compiler will handily point out exactly which patterns no longer make any 
 sense or which ones are now non-exhaustive. Fixing it straight forward.


 You mention the value of the compiler pointing out non-exhaustive pattern 
 matching. It might interest you to know that in Erlang that is a no-no. Your 
 pattern matching should not handle cases you do not know how to deal with in 
 a let it crash manner. Actually Joe Armstrong (Erlang's creator) puts it as 
 let some other process fix the error. The idea is not unlike the idea common 
 in exception handling that you shouldn't catch all exceptions but rather 
 those that you can handle.


 Crash here doesn't mean bring down the program. In Erlang, a program 
 typically consists of a number of processes, interacting via message passing. 
 If a process crashes then another process handles that event 
 (oversimplified). It may elect to respawn the process crashed and/or deal 
 with the error in whatever way it sees fit. It may also simply die as well, 
 and then its supervisor handles the result. As I said, the nearest thing to 
 this is other languages is exception handling.

 The whole point, as it is the point in exception handling, is that the 
 process or function should not be programmed that defensively. Let's have an 
 example. The next is F# code:

 let testColor c =
    match c with
    | Black - 1
    | White - 0
    | Other - failwith unexpected color

 an almost equivalent Erlang code is

 case c of
    black - 1;
    white - 0
 end;

 In F#, I must use the failwith function. The reason is that the language 
 forces me to have exhaustive pattern matching. In Erlang it doesn't. In fact, 
 Erlang encourages me not to fill in a handler for all other cases where the 
 color is not black or white.

 Now the results:
 - In F#, I know that my function has handled all cases. I can depend on that. 
 On the other hand, because I was forced to do what is essentially error 
 handling as early as possible I might get it wrong. In the example, I 
 returned a generic error unexpected color which may or may not be true (in 
 this case copied from here I am using an active pattern and the error might 
 be anything else in the pattern function). In short, I am losing information 
 because I had to handle the potential error early. This is the same argument 
 in exception handling. You don't catch an exception which you do not know how 
 to handle; you let it go up the stack.
 - In Erlang, if a colour is not matched then the pattern matching raises a 
 badmatch error. This can be handled at the appropriate level in the function 
 stack or the process hierarchy.
 - On the other hand, the F# approach is probably more amenable to proof of 
 correctness. Depending on whether you think this is an important enough 
 issue, you might prefer that.

 I have worked with both. It really depends on the whole approach you are 
 using. Erlang has a very impressive pattern matching and it is 
 non-exhaustive, because the whole model of fault handling is different from 
 the ML approach where a fault is a world collapses type of event. 
 Armstrong's PhD thesis was Making reliable distributed systems in the 
 presence of software errors. The ML approach is to not have software errors.

 The point of the Erlang philosophy is not to produce bad code that always 
 crashes. Let it crash