Re: Desired Language Features

dekker@dutiag.twi.tudelft.nl (Rene Dekker)
Mon, 4 Oct 1993 10:05:58 GMT

          From comp.compilers

Related articles
Desrired Language Features amn@ubik.demon.co.uk (1993-10-02)
Re: Desired Language Features dekker@dutiag.twi.tudelft.nl (1993-10-04)
Re: Desired Language Features henry@zoo.toronto.edu (1993-10-06)
Re: Desired Language Features doug@netcom.com (1993-10-19)
Re: Desired Language Features throop@aurw44.aur.alcatel.com (1993-10-20)
| List of all articles for this month |
Newsgroups: comp.compilers
From: dekker@dutiag.twi.tudelft.nl (Rene Dekker)
Keywords: design, C, errors
Organization: Delft University of Technology
References: 93-10-021
Date: Mon, 4 Oct 1993 10:05:58 GMT

amn@ubik.demon.co.uk (Anthony Naggs) writes:


>[desirable feature of programming languages are:]
><1> Errors and procedures can indicate a failure, (with the common assembly
> language technique of setting/clearing the Carry flag appropriately)
><2> Local exception handling using an 'error' block, for minor problems
> that are easily resolved
><3> Global error handling, for serious problems. Possibly by extending the
> error block to inorporate Ada's solid model, ('raise' & 'exception').
[examples deleted]


All of the above can be done with an ADA-like exception mechanism. I
don't see the point in having a separate mechanism for `local' and
`global' exceptions. From the point of view of language design, this
only clutters the language and is conceptually not very clean. Your
only benefit is that the `local' mechanism might be compiled into more
efficient code.
Although I know that optimizing exceptions is difficult stuff, I think
a good compiler good see the difference between a local exception and a
global one, and could optimize appropriately. But the real compiler
guys might have a different view of this. By the way, exceptions
should not occur enough to justify an optimally efficient
implementation.
For whom it may concern, I have included my exception package for C
below. It pretty much implements the Ada-style mechanism. And it is in
the form of a C-include file, not a language adaption. It contains
some comments, but if you have questions about it, feel free to mail
me.


><4> Three way comparisons, eg:
> compare (value, reference)
> {
> case < : /* value < reference */
> case = : /* value = reference */
> case > : /* value > reference */
> }


>From the language design point of view, this only gives you the benefit
of a different syntax. Note that you can achieve something similar with
the following definitions:


#define CMP( a, b ) ( (a) < (b) ? '<' : ((a) > (b) ? '>' : '=') )
switch( CMP(value, reference) )
{
      case '<': /* value < reference */
      case '=': /* value = reference */
      case '>': /* value > reference */
}
(Better substitute -1, 0 and 1 for '<', '=' and '>' if you like to
stay in the style of C).


Again, a good compiler might be able to compile this almost as
efficient as your three-way comparison. Note that designing a language
that supports extension to include your compare concept is far better
than `hard coding' it into the language.


><5> Language support for testing input parameters, to replace the normal
> kludges of nested if's or 'do .. while (0)', etc... This is essential
> for any formal verification of program operation, (eg to compare a
> program with a first order predicate logic design in VDM or as a Z
> schema). For example, using a 'test' block with 'require' & 'accept';
[`accept' indicates predicates whose validity inhibits other tests, and
`require' indicates predicates that must be true among other predicates.]


Now, this is the more interesting stuff. Your require() and accept()
are certainly interesting, but I beg to differ if the "normal" way of
doing these pre- and post-conditions (as in the Eiffel language) is any
less clear. Here is an example, thrown into my exception notation:


int foo( int value1, int value2, int value3 )
{
      if(
            value2 > value 3 &&
            (value1 == value3 ||
              value2 > 0 ? value3 == 0 : value != value2)
      ) raise( WEIRD_ARGUMENTS );
      ...
}


Of course, a separate syntax might be desirable for formal
verification purposes.


Don't get me wrong, your ideas certainly make sense. But take under
consideration that designing a new language is not something to think
lightly about. It is not a matter of `adding a few features.' A new
language with new features usually has those features right, but has flaws
on other points. Furthermore, you need to consider things like ease of
programming, ease of understanding, ease of maintenance, whether your
features fit into the language, whether they are combinable with the other
language features, whether they improve the programming style, and so on.
As an example, formal verification of the validity of function arguments
does not fit very well with the C-language. Look at Ada for both a good
and bad example of language design.


Designing an exception mechanism is especially difficult. Exceptions
should be used sparcely, so as to preserve the structure of the code. A
very powerful exception mechanism might induce a spaghetti style of
programming, much the way powerful goto's do.


As to speak with the words of our moderator:
The world is not waiting for yet another language.


Ciao,
Rene.


Here is the exception package:


/*
      EXCEPT.H -- An exception package


      Created by Rene Dekker, October 1992


      Two parties are involved in an exception: the routine that raises
      the exception (the pitcher) and the routine that catches it (the
      catcher). Here are example pitcher and catcher files:


      PITCHER.H:
            #include <except.h>
            extern exception WATER_BOILS;
            extern exception KETTLE_BURSTS;


      PITCHER.C:
            #include "pitcher.h"
            exception WATER_BOILS;
            exception KETTLE_BURSTS;


            void try_to_heat_the_water( void )
            {
                  ...
                  if( temperature >= 100 )
                  {
                        if( waterlevel == 0 )
                              raise( KETTLE_BURSTS );
                        raise( WATER_BOILS );
                  }
                  ...
            }


      CATCHER.C:
            #include "pitcher.h"


                  beg_exc
                        try_to_heat_the_water( );
                        ...
                  on_exc( WATER_BOILS )
                        burn_hands( );
                  on_others
                        panic( );
                        printf( "Exception: %s\n", the_exception->name );
                        reraise;
                  end_exc /* <- NO semicolon !! */


      Some remarks:
      - Multiple exceptions can be handled in one construct. Exceptions
      that are not handled are automaticaly reraised.
      - It is possible to perform a raise, break or return (or goto)
      during the handling of an exception. A break ends the exception
      construct. A return ends the containing function.
      - A break in the body of an exception construct ends the construct.
      Return (or goto) must NOT be used within the body. (sorry)
      - A reraise conserves the information of the original exception,
      a raise of the same exception does not. Otherwise, there is no
      difference.
      - There is a on_others phrase that catches all exceptions. Reraise
      works as expected in such a phrase.
      - There is a symbol the_exception that contains a pointer to the
      current exception. You can use this pointer to retrieve information
      or choose action or whatever you like.
      - The names beg_exc and end_exc are deliberately chosen like that
      to keep the common names begin and end available.
      - Should be adapted so that it works with pre-ANSI also.
*/


#ifndef EXCEPT_H
#define EXCEPT_H


#include <setjmp.h>


#define beg_exc \
      { do { /* for breaks */
            _catcher _catch, *_oldcatcher = _currcatcher; \
            _currcatcher = &_catch; \
            if( setjmp(_catch.buf) == 0 ) {
#define on_exc( EXC ) \
            } else if( _catch.exc == &(EXC) ) { \
                  _currcatcher = _oldcatcher;
#define on_others \
            } else if( _catch.exc != NULL ) { \
                  _currcatcher = _oldcatcher;
#define end_exc \
            } else { \
                  _currcatcher = _oldcatcher; reraise; \
            } } while( 1 ); \
            _currcatcher = _oldcatcher; \
      }


#define raise( EXC ) \
      do { EXC.name = #EXC; EXC.file = __FILE__; EXC.line = __LINE__; \
                _currcatcher->exc = &EXC; longjmp( _currcatcher->buf, 1 ); \
      } while( 0 )
#define reraise \
      do { _currcatcher->exc = _catch.exc; longjmp( _currcatcher->buf, 1 ); \
      } while( 0 )


/*
      exception const *the_exception;
*/
#define the_exception ( _catch.exc )


typedef struct {
      const char *name;
      const char *file;
      long line;
} exception;


typedef struct {
      exception *exc;
      jmp_buf buf;
} _catcher;


extern _catcher *_currcatcher;


#endif
--
Rene Dekker Delft University of Technology r.dekker@cs.tudelft.nl
------------------------------------------------------------------------
If life seems jolly rotten, there's something you've forgotten!
And that's to laugh and smile and dance and sing...


--


Post a followup to this message

Return to the comp.compilers page.
Search the comp.compilers archives again.