www.digitalmars.com         C & C++   DMDScript  

D - Suggestion: "if" outside of function blocks (and much more)

reply Norbert Nemec <nobbi_at_theorie3.physik.uni-erlangen.de NOSPAM.COM> writes:
Hi there,

just a quick and dirty suggestion:

How about allowing "if"-"else"-blocks outside of function blocks? Of course, 
the argument will have to be computable at compile time.

This simple construct will allow several things:

* "version" could become a boolean expression instead of a block statement.
        version(...) {
        }
  would become
        if(version(...)) {
        }
  with the added possibily of logic constructions.

* Together with integers as template parameters, the data fields of a 
template class could be dependant on those parameters.

The addition should be absolutely straightforward. Anyhow a few more 
additions would make it even more powerful:

* Define "compile-time-value" as any value that can be deduced by the 
compiler.

* Define "function without side-effects" (should be an attribute of some 
kind)

* Require the compiler to be able to run such functions at compile time if 
the arguments are compile-time-value

Now, every expression made up of constants, basic operators and functions 
without side-effects are compile-time-values.

This all taken together would perhaps give us all the 
compile-time-computing-power we need to put really heavy optimization into 
a library.

One more detail: There should be an argument for function arguments to be 
specified as compile-time-values. This way, the function can only be called 
with a compile-time-value provided at that point, and inside the function, 
the value can be used, p.e. as a template parameter.

And a last thought: it might prove useful to provide a larger set of 
expressions for handling types, like comparing them, etc. so these can be 
used in compile-time decisions within a template library.

Ciao,
Nobbi
Jan 17 2003
next sibling parent reply Evan McClanahan <evan dontSPAMaltarinteractive.com> writes:
Comments embedded.

Norbert Nemec wrote:
 Hi there,
 
 just a quick and dirty suggestion:
 
 How about allowing "if"-"else"-blocks outside of function blocks? Of course, 
 the argument will have to be computable at compile time.
 
 This simple construct will allow several things:
 
 * "version" could become a boolean expression instead of a block statement.
         version(...) {
         }
   would become
         if(version(...)) {
         }
   with the added possibily of logic constructions.
This seems like the mechanism already in place, I don't really see the win there. Unless I'm wrong and you can't do version(..1) { } version(..2) { } else { } in which case I see where you're going and approve.
 * Together with integers as template parameters, the data fields of a 
 template class could be dependant on those parameters.
 
 The addition should be absolutely straightforward. Anyhow a few more 
 additions would make it even more powerful:
 
 * Define "compile-time-value" as any value that can be deduced by the 
 compiler.
 
 * Define "function without side-effects" (should be an attribute of some 
 kind)
 
 * Require the compiler to be able to run such functions at compile time if 
 the arguments are compile-time-value
hard to do, chews up keywords. Hard to make guarantees in those respects, I think. It would also be *really* annoying to maintain. But then, I'm not really interested in writing libraries, so if it didn't intrude much, I'd not really complain.
 Now, every expression made up of constants, basic operators and functions 
 without side-effects are compile-time-values.
 
 This all taken together would perhaps give us all the 
 compile-time-computing-power we need to put really heavy optimization into 
 a library.
 
 One more detail: There should be an argument for function arguments to be 
 specified as compile-time-values. This way, the function can only be called 
 with a compile-time-value provided at that point, and inside the function, 
 the value can be used, p.e. as a template parameter.
 
 And a last thought: it might prove useful to provide a larger set of 
 expressions for handling types, like comparing them, etc. so these can be 
 used in compile-time decisions within a template library.
 
 Ciao,
 Nobbi
 
Jan 17 2003
parent Norbert Nemec <nobbi_at_theorie3.physik.uni-erlangen.de NOSPAM.COM> writes:
Evan McClanahan wrote:

 Comments embedded.
 
 Norbert Nemec wrote:
 Hi there,
 
 just a quick and dirty suggestion:
 
 How about allowing "if"-"else"-blocks outside of function blocks? Of
 course, the argument will have to be computable at compile time.
 
 This simple construct will allow several things:
 
 * "version" could become a boolean expression instead of a block
 statement.
         version(...) {
         }
   would become
         if(version(...)) {
         }
   with the added possibily of logic constructions.
This seems like the mechanism already in place, I don't really see the win there. Unless I'm wrong and you can't do version(..1) { } version(..2) { } else { } in which case I see where you're going and approve.
There's no win at that point. Just that the language definition becomes simpler if "version" is not an additional block statement, but simply a expression evaluating to a boolean. And my generalized "if" is by far more powerful, and maybe even easier to understand, than the current "version"-block statement. The language definition gets tidier and the result is far more expressive.
 * Define "compile-time-value" as any value that can be deduced by the 
 compiler.
 
 * Define "function without side-effects" (should be an attribute of some 
 kind)
 
 * Require the compiler to be able to run such functions at compile time
 if the arguments are compile-time-values
hard to do, chews up keywords.
???!!! "const" is there already. One keyword for "function without side-effects" would have to be found (suggestions?). That's it.
 Hard to make guarantees in those respects, I think.
 It would also be *really* annoying to maintain.  But
 then, I'm not really interested in writing libraries, so if it didn't
 intrude much, I'd not really complain.
What do you mean? Maintain the code? Nothing special to do there. I just propose the additional possibility to give a special attribute to some functions so they can be used in places where the result has to be know at compile time. This doesn't restrict the use of the function, just what you can do within. And since, usually, you would't want to do expensive computations at compile-time anyway, it is mainly meant for tiny, simple functions after all. So where's the problem? Of course, the compiler needs some additional intelligence, but about all that is needed is there already for optimization. Basically, what I'm talking about, is constant folding and inlining. About every reasonable compiler has those, so the extra effort is not that bad. What is needed, is to define what exactly is allowed in a function without side-effects and assure that the compiler can do those things at compile time. Ciao, Nobbi
Jan 17 2003
prev sibling parent reply "Mike Wynn" <mike.wynn l8night.co.uk> writes:
this is something I have been thinking about, allowing D to be used for
runtime and compile time code.
allowing sections of code, and variables to exists at compile time (a D
interpreter as part of the D compile, the ast/dag could be interpreted
instead of compiled)
you can perform all the features of C preprocessor macros and inline
functions etc;
I think allowing a clear distinction between runtime and compile time values
and code may seem odd to C/C++ programmers and is a feature I've not seen in
any languages (has anyone?), but is something I've wanted in several.

compiletime { .... compile time executed D ... }

so to create a static array of constants

Object[] foo;
// may have to be compiletime(visible) Object[] foo;
// might be compiletime( only ) {
// or compiletime( eval ) {
compiletime {
       /// perfroms the same op as c++ [cost] Object[] = { { 1, 2 }, { 2,
4, } ..... }; but with more flexability
        /// will actually create a "snap shot" in rom/code/ram of the
compiled object mesh.
    foo = new MyObject[5];
    for ( int i = 0; i < 5; i++ )
    {
        foo[i] = new MyObject( i, myFunc( i*2 ) );
    }
}

compiletime int min( int a, int b ) { return a < b ? a : b ; }
would only compile if a and b are determinable at compile time, in the
current context

etc and so on

it would be good if templates could have parameter arrays too

template foo ( T, P )
{
    T myFunc( P )
    {
        compiletime {
            if ( cast( ctParams[]) P )
            {
                    if ( !(P[0].hasoperator( +, T  ) ) ) throw new
CompileTimeError( "Param 0 must have and operator + for foo.MyFunc to
work" );
                runtime {
                       T = compileTime P[0] + compileTime P[1];
                }
            }
            else
                throw CompileTimeError( "P must be a param list type " );
        }
    }
}

"Norbert Nemec" <nobbi_at_theorie3.physik.uni-erlangen.de NOSPAM.COM> wrote
in message news:b090su$2qf4$1 digitaldaemon.com...
 Hi there,

 just a quick and dirty suggestion:

 How about allowing "if"-"else"-blocks outside of function blocks? Of
course,
 the argument will have to be computable at compile time.

 This simple construct will allow several things:

 * "version" could become a boolean expression instead of a block
statement.
         version(...) {
         }
   would become
         if(version(...)) {
         }
   with the added possibily of logic constructions.

 * Together with integers as template parameters, the data fields of a
 template class could be dependant on those parameters.

 The addition should be absolutely straightforward. Anyhow a few more
 additions would make it even more powerful:

 * Define "compile-time-value" as any value that can be deduced by the
 compiler.

 * Define "function without side-effects" (should be an attribute of some
 kind)

 * Require the compiler to be able to run such functions at compile time if
 the arguments are compile-time-value

 Now, every expression made up of constants, basic operators and functions
 without side-effects are compile-time-values.

 This all taken together would perhaps give us all the
 compile-time-computing-power we need to put really heavy optimization into
 a library.

 One more detail: There should be an argument for function arguments to be
 specified as compile-time-values. This way, the function can only be
called
 with a compile-time-value provided at that point, and inside the function,
 the value can be used, p.e. as a template parameter.

 And a last thought: it might prove useful to provide a larger set of
 expressions for handling types, like comparing them, etc. so these can be
 used in compile-time decisions within a template library.

 Ciao,
 Nobbi
Jan 17 2003
next sibling parent reply Norbert Nemec <Nobbi_at_theorie3.physik.uni-erlangen.de NOSPAM.COM> writes:
Mike Wynn wrote:

 this is something I have been thinking about, allowing D to be used for
 runtime and compile time code.
 allowing sections of code, and variables to exists at compile time (a D
 interpreter as part of the D compile, the ast/dag could be interpreted
 instead of compiled)
 you can perform all the features of C preprocessor macros and inline
 functions etc;
 I think allowing a clear distinction between runtime and compile time
 values and code may seem odd to C/C++ programmers and is a feature I've
 not seen in any languages (has anyone?), but is something I've wanted in
 several.
Well, part of it is present in many languages. C++ allows a const to be calculated as an expression of other consts. I never thought of any special "compiletime" functions, but just of functions that are allowed in such a constant expression. Many language features (like pointer handling etc.) would have to be prohibited in those restricted functions, but otherwise they are just plain functions usuable at any point in the code. The only point where the whole thing goes beyond plain constant folding is, that via "if" statements outside of functions, there can be arbitrary decisions made at compile time about the code that is to be produced. Anyhow, the idea of a "CompileTimeError" is something I missed. Of course this would be necessary - although I don't think "throw" should be used for it. It should rather be done by a separate statement. Ciao, Nobbi
Jan 17 2003
parent reply "Mike Wynn" <mike.wynn l8night.co.uk> writes:
"Norbert Nemec" <Nobbi_at_theorie3.physik.uni-erlangen.de NOSPAM.COM> wrote
in message news:b0a9i8$jm6$1 digitaldaemon.com...
 Mike Wynn wrote:

 this is something I have been thinking about, allowing D to be used for
 runtime and compile time code.
 allowing sections of code, and variables to exists at compile time (a D
 interpreter as part of the D compile, the ast/dag could be interpreted
 instead of compiled)
 you can perform all the features of C preprocessor macros and inline
 functions etc;
 I think allowing a clear distinction between runtime and compile time
 values and code may seem odd to C/C++ programmers and is a feature I've
 not seen in any languages (has anyone?), but is something I've wanted in
 several.
Well, part of it is present in many languages. C++ allows a const to be calculated as an expression of other consts. I never thought of any
special
 "compiletime" functions, but just of functions that are allowed in such a
 constant expression. Many language features (like pointer handling etc.)
 would have to be prohibited in those restricted functions, but otherwise
 they are just plain functions usuable at any point in the code. The only
 point where the whole thing goes beyond plain constant folding is, that
via
 "if" statements outside of functions, there can be arbitrary decisions
made
 at compile time about the code that is to be produced.
exactly my point, parts are present in other langs, but not the concept that code can be run at either compile time or runtime explicitly. why stop at 'if', if you have if, you need to have expressions, many statements can be written as expressions; infact many programs can be rewritten to be one expression, so not why go that extra step; this allows manual control over constant folding allows you to write lookuptables in the same code as the program; think of it this way, you can write a sin lookuptable (float ->fixed compile time conversion) that is build by the compiler to run on a platform without floats, but without having to write two programs. no more writing programs to generate source code, for the compiler to compile, you write is all in one program, easy to maintain, easy to modify when things change 0..360 deg lookup not 0..256 all the consts that the lookuptable gen code sees are all the same values that the final code sees pointers to static data are known at compile time (or enough is know about them to link later) so why not allow compile time new that allocate in the final code/rom image ?
 Anyhow, the idea of a "CompileTimeError" is something I missed. Of course
 this would be necessary - although I don't think "throw" should be used
for
 it. It should rather be done by a separate statement.
Why ? that's what exceptions are for, I can not continue along this code path - goto somewhere (anywhere) that can deal with the problem. seem simple and logical (to me). why add yet another keyword/feature when there is a perfect solution already there. all though I do see a problem with 'new' which would create the exception object in the final image, but thats o.k. because the compile is being terminated, if the exception is caught, that's also o.k. the new'ed objects would only need one gc mark-sweep cycle to remove unreachables before comitting the data to the object file image. Mike.
Jan 17 2003
parent reply Norbert Nemec <Nobbi_at_theorie3.physik.uni-erlangen.de NOSPAM.COM> writes:
Mike Wynn wrote:
 why stop at 'if'...
Be careful on how far you take the concept. My suggestions were tiny, easy to implement extensions to the current language. Of course, one can always start dreaming, but in the end, the whole suggestion will just be to invading and complex to be realistic. D is not a language to experiment with great, new ideas, but to stay in well-known terrain and make the best out of it. I think, my few suggestions were pretty straightforward and one can about estimate their overall effects on the overall language, but once you get beyond that point, there's the danger that many more extensions have to be added to compensate effects you never dreamed of. Just think of templates in C++. They were a pretty revolutionary concept when they were invented, and that is the cause for most of the problems of complexity they introduce. Ciao, Nobbi
Jan 18 2003
parent "Mike Wynn" <mike.wynn l8night.co.uk> writes:
"Norbert Nemec" <Nobbi_at_theorie3.physik.uni-erlangen.de NOSPAM.COM> wrote
in message news:b0bdua$185s$1 digitaldaemon.com...
 Mike Wynn wrote:
 why stop at 'if'...
Be careful on how far you take the concept. My suggestions were tiny, easy to implement extensions to the current language. Of course, one can always start dreaming, but in the end, the whole suggestion will just be to invading and complex to be realistic. D is not a language to experiment with great, new ideas, but to stay in well-known terrain and make the best out of it.
the think is parts are already there consider bit a = (SOME_CONST < 5) || (SOMEOTHER>600); which is infact bit a; if ( SOME_CONST < 5 ) { a = true;} else if (SOMEOTHER>600){ a = true } else { a = false; } more fun, D has the C comma operator; I must try out const int a = 3; int b; int c = (b = (a+3)), a+5; it does not work (not that I'm suprised) const int a = 3; int b; int c = (b = a); gives "test.d(6): non-constant expression b = 3" but int d = a+5; bit e = (a<3)||(a>67); compile fine
 I think, my few suggestions were pretty straightforward and one can about
 estimate their overall effects on the overall language, but once you get
 beyond that point, there's the danger that many more extensions have to be
 added to compensate effects you never dreamed of.
your right, but conditional compilation exists in D with the use of 'version( foo ) { ... }' and I've realised that what I realy want in const functions and constructors; that is a function whose output is ONLY determined by its inputs and has no effect on anything other than its locals and return value. the same for constructors, which only effect the object, and could only call const constructors of members. this means that as long as the params are fully determinable at compile time, the return of the function or the consturcted object would also be fully determinable at compile time.
 Just think of templates in C++. They were a pretty revolutionary concept
 when they were invented, and that is the cause for most of the problems of
 complexity they introduce.
and for all their faults and complex probems they introduce they are now a standard feature that people expect a new language to have.
Jan 18 2003
prev sibling next sibling parent reply "Daniel Yokomiso" <daniel_yokomiso yahoo.com.br> writes:
"Mike Wynn" <mike.wynn l8night.co.uk> escreveu na mensagem
news:b09ou3$9jj$1 digitaldaemon.com...
 this is something I have been thinking about, allowing D to be used for
 runtime and compile time code.
 allowing sections of code, and variables to exists at compile time (a D
 interpreter as part of the D compile, the ast/dag could be interpreted
 instead of compiled)
 you can perform all the features of C preprocessor macros and inline
 functions etc;
 I think allowing a clear distinction between runtime and compile time
values
 and code may seem odd to C/C++ programmers and is a feature I've not seen
in
 any languages (has anyone?), but is something I've wanted in several.
Syntax definitions in Scheme are macro expanded at compile-time but accessing the entire environment while doing so. http://www.scheme.com/tspl2d/ and http://www.schemers.org/Documents/Standards/R5RS/ can give you directions. They're different from normal function definitions, but can be used as language primitives.
 compiletime { .... compile time executed D ... }

 so to create a static array of constants

 Object[] foo;
 // may have to be compiletime(visible) Object[] foo;
 // might be compiletime( only ) {
 // or compiletime( eval ) {
 compiletime {
        /// perfroms the same op as c++ [cost] Object[] = { { 1, 2 }, { 2,
 4, } ..... }; but with more flexability
         /// will actually create a "snap shot" in rom/code/ram of the
 compiled object mesh.
     foo = new MyObject[5];
     for ( int i = 0; i < 5; i++ )
     {
         foo[i] = new MyObject( i, myFunc( i*2 ) );
     }
 }

 compiletime int min( int a, int b ) { return a < b ? a : b ; }
 would only compile if a and b are determinable at compile time, in the
 current context

 etc and so on

 it would be good if templates could have parameter arrays too

 template foo ( T, P )
 {
     T myFunc( P )
     {
         compiletime {
             if ( cast( ctParams[]) P )
             {
                     if ( !(P[0].hasoperator( +, T  ) ) ) throw new
 CompileTimeError( "Param 0 must have and operator + for foo.MyFunc to
 work" );
                 runtime {
                        T = compileTime P[0] + compileTime P[1];
                 }
             }
             else
                 throw CompileTimeError( "P must be a param list type " );
         }
     }
 }

 "Norbert Nemec" <nobbi_at_theorie3.physik.uni-erlangen.de NOSPAM.COM>
wrote
 in message news:b090su$2qf4$1 digitaldaemon.com...
 Hi there,

 just a quick and dirty suggestion:

 How about allowing "if"-"else"-blocks outside of function blocks? Of
course,
 the argument will have to be computable at compile time.

 This simple construct will allow several things:

 * "version" could become a boolean expression instead of a block
statement.
         version(...) {
         }
   would become
         if(version(...)) {
         }
   with the added possibily of logic constructions.

 * Together with integers as template parameters, the data fields of a
 template class could be dependant on those parameters.

 The addition should be absolutely straightforward. Anyhow a few more
 additions would make it even more powerful:

 * Define "compile-time-value" as any value that can be deduced by the
 compiler.

 * Define "function without side-effects" (should be an attribute of some
 kind)

 * Require the compiler to be able to run such functions at compile time
if
 the arguments are compile-time-value

 Now, every expression made up of constants, basic operators and
functions
 without side-effects are compile-time-values.

 This all taken together would perhaps give us all the
 compile-time-computing-power we need to put really heavy optimization
into
 a library.

 One more detail: There should be an argument for function arguments to
be
 specified as compile-time-values. This way, the function can only be
called
 with a compile-time-value provided at that point, and inside the
function,
 the value can be used, p.e. as a template parameter.

 And a last thought: it might prove useful to provide a larger set of
 expressions for handling types, like comparing them, etc. so these can
be
 used in compile-time decisions within a template library.

 Ciao,
 Nobbi
--- Outgoing mail is certified Virus Free. Checked by AVG anti-virus system (http://www.grisoft.com). Version: 6.0.443 / Virus Database: 248 - Release Date: 11/1/2003
Jan 17 2003
parent "Mike Wynn" <mike.wynn l8night.co.uk> writes:
 "Mike Wynn" <mike.wynn l8night.co.uk> escreveu na mensagem
 news:b09ou3$9jj$1 digitaldaemon.com...
 this is something I have been thinking about, allowing D to be used for
 runtime and compile time code.
 allowing sections of code, and variables to exists at compile time (a D
 interpreter as part of the D compile, the ast/dag could be interpreted
 instead of compiled)
 you can perform all the features of C preprocessor macros and inline
 functions etc;
 I think allowing a clear distinction between runtime and compile time
values
 and code may seem odd to C/C++ programmers and is a feature I've not
seen
 in
 any languages (has anyone?), but is something I've wanted in several.
Syntax definitions in Scheme are macro expanded at compile-time but accessing the entire environment while doing so. http://www.scheme.com/tspl2d/ and http://www.schemers.org/Documents/Standards/R5RS/ can give you directions. They're different from normal function definitions, but can be used as language primitives.
I must read that more fully, but looks very much along the lines I was thinking about. I would like scheme if it is was not for the prolific use of brackets, and the lack of list indexed access (all those cddar's cry out for c[2] in my mind (wrote some gimp scripts, that was enough scheme for me)).
Jan 18 2003
prev sibling parent "Sean L. Palmer" <seanpalmer directvinternet.com> writes:
Yes;  this feature of Boost::mpl is extremely handy.

Sean

"Mike Wynn" <mike.wynn l8night.co.uk> wrote in message
news:b09ou3$9jj$1 digitaldaemon.com...
 it would be good if templates could have parameter arrays too
Jan 18 2003