www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Compile-time exceptions

reply bearophile <bearophileHUGS lycos.com> writes:
This is a generic example of a small function template (it's similar to a
function with the same name in Mathematica. It that returns the result of the
callable 'func' applied n times to 'item'):


TyItem nest(TyFun, TyItem)(TyFun func, TyItem item, int n) {
    static assert(IsCallable!(TyFun), "nest(): func must be a callable");
    static assert(CastableTypes!(ReturnType!(TyFun), TyItem),
                  "nest(): func must return a type castable to item.");
    if (n < 0)
        throw new ArgumentException("nestList(): n must be >= 0");

    for (int i = 1; i <= n; i++)
        item = func(item);
    return item;
} // End of nest()


Its unit tests contain the following stuff too, because raising that exception
is part of the user interface of that function:

unittest {
    ...
    assert(Throws!(ArgumentException)(nest(&sin, 0.1, -1)));
    ...
    //nest("hello", 0.1, -1); // static asserts
    ...
}

(I'm waiting to have a better Throws!() in the std lib that doesn't require to
be put into an assert()).
But at the moment I haven't found ways to unit test the first two static
asserts, so I just put tests in the unittest for them, and I comment them out
to make the unittest run.

That problem may be solved with a "static try" and "static catch", that work at
compile time :-)
(I think this isn't related with the Java static exceptions).

static try
    deleg();
static catch (StaticAssert a) {
    ...
}

Bye,
bearophile
Nov 24 2008
next sibling parent "Denis Koroskin" <2korden gmail.com> writes:
On Mon, 24 Nov 2008 13:15:36 +0300, bearophile <bearophileHUGS lycos.com>  
wrote:

 This is a generic example of a small function template (it's similar to  
 a function with the same name in Mathematica. It that returns the result  
 of the callable 'func' applied n times to 'item'):


 TyItem nest(TyFun, TyItem)(TyFun func, TyItem item, int n) {
     static assert(IsCallable!(TyFun), "nest(): func must be a callable");
     static assert(CastableTypes!(ReturnType!(TyFun), TyItem),
                   "nest(): func must return a type castable to item.");
     if (n < 0)
         throw new ArgumentException("nestList(): n must be >= 0");

     for (int i = 1; i <= n; i++)
         item = func(item);
     return item;
 } // End of nest()


 Its unit tests contain the following stuff too, because raising that  
 exception is part of the user interface of that function:

 unittest {
     ...
     assert(Throws!(ArgumentException)(nest(&sin, 0.1, -1)));
     ...
     //nest("hello", 0.1, -1); // static asserts
     ...
 }

 (I'm waiting to have a better Throws!() in the std lib that doesn't  
 require to be put into an assert()).
 But at the moment I haven't found ways to unit test the first two static  
 asserts, so I just put tests in the unittest for them, and I comment  
 them out to make the unittest run.

 That problem may be solved with a "static try" and "static catch", that  
 work at compile time :-)
 (I think this isn't related with the Java static exceptions).

 static try
     deleg();
 static catch (StaticAssert a) {
     ...
 }

 Bye,
 bearophile

This has already been discussed: http://digitalmars.com/d/archives/digitalmars/D/static_try_catch_construct_would_be_helpful_66794.html
Nov 24 2008
prev sibling parent reply Christopher Wright <dhasenan gmail.com> writes:
bearophile wrote:
 But at the moment I haven't found ways to unit test the first two static
asserts, so I just put tests in the unittest for them, and I comment them out
to make the unittest run.

assert (!is (typeof (nest(1, 1, 1)))); // int is not callable If you're using d2: assert (!__traits (compiles, nest(1, 1, 1)));
Nov 24 2008
next sibling parent bearophile <bearophileHUGS lycos.com> writes:
Christopher Wright:
 assert (!is (typeof (nest(1, 1, 1)))); // int is not callable

Oh, right, that syntax isn't much elegant, but it seems to work, and it's doable. Thank you. I'll probably use it now and then. You can use it statically too :-) static assert (!is (typeof (nest(1, 2, 3)))); // int is not callable
If you're using d2:<

Not yet. Bye, bearophile
Nov 24 2008
prev sibling next sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
Christopher Wright Wrote:
 assert (!is (typeof (nest(1, 1, 1)))); // int is not callable

I have just used a static assert version of that in about one hundred places in my dlibs, with good results, thank you :-) It has replaced lot of commented out and mostly useless lines of code with actual code that can spot problems at compile time. This begs for compiler (or standard lib) support, I think. On the topic of idioms: I may also use an idiom just shown to me by downs, to allow "passing" "function templates" to other functions: _sum { T opCall(T)(T[] array) { ... } } _sum sum; That's another situation where I think some built-in syntactic sugar can be useful. Do you have ideas regarding a possible syntax/statement? Bye, bearophile
Nov 25 2008
parent reply Christopher Wright <dhasenan gmail.com> writes:
bearophile wrote:
 On the topic of idioms: I may also use an idiom just shown to me by downs, to
allow "passing" "function templates" to other functions:
 _sum { T opCall(T)(T[] array) { ... } } _sum sum;

I don't know what this is doing. That code fragment won't compile, and it isn't showing usage. Passing an instantiation of a function template is easy -- you just pass in a function. Passing a template requires an alias template parameter, I think. Virtual templates would allow you to use polymorphism in this case, but that's not going to be considered for quite some time, if at all.
 That's another situation where I think some built-in syntactic sugar can be
useful. Do you have ideas regarding a possible syntax/statement?
 
 Bye,
 bearophile

Nov 25 2008
parent reply bearophile <bearophile lycos.com> writes:
Christopher Wright Wrote:
 I don't know what this is doing. That code fragment won't compile, and 
 it isn't showing usage.

Sorry: struct _add { T opCall(T.init + T.init)(T x) { return x+x; } } _add add; Now you can give it to a function, without the need of specializing it for a type T first (you can't give the pointer to a template). (This may also be faster, because there isn't a delegate to call). (Haskell is statically typed and allows you do more complex things with a clean enough syntax).
 Virtual templates would allow you to use polymorphism in this case, but 
 that's not going to be considered for quite some time, if at all.

Can you explain? Bye, bearophile
Nov 25 2008
next sibling parent bearophile <bearophileHUGS lycos.com> writes:
Denis Koroskin:
 Did you mean:

Yes, of course :-) Bye, bearophile
Nov 25 2008
prev sibling parent Christopher Wright <dhasenan gmail.com> writes:
bearophile wrote:
 Christopher Wright Wrote:
 I don't know what this is doing. That code fragment won't compile, and 
 it isn't showing usage.

Sorry: struct _add { T opCall(T.init + T.init)(T x) { return x+x; } } _add add; Now you can give it to a function, without the need of specializing it for a type T first (you can't give the pointer to a template). (This may also be faster, because there isn't a delegate to call). (Haskell is statically typed and allows you do more complex things with a clean enough syntax).

I thought you could do this with an alias parameter, but perhaps I'm wrong. It's not the sort of thing I try to do, usually.
 Virtual templates would allow you to use polymorphism in this case, but 
 that's not going to be considered for quite some time, if at all.

Can you explain?

Like generics in C#. Java's just hiding casts with its generics, but C# does specialized code generation at runtime for each generic type it creates. D could do this with full templates, not just generics, and at compile time, but it'd require an extra compilation step before linking, most likely.
 Bye,
 bearophile

Nov 25 2008
prev sibling parent "Denis Koroskin" <2korden gmail.com> writes:
On Tue, 25 Nov 2008 15:28:53 +0300, bearophile <bearophile lycos.com>  
wrote:

 Christopher Wright Wrote:
 I don't know what this is doing. That code fragment won't compile, and
 it isn't showing usage.

Sorry: struct _add { T opCall(T.init + T.init)(T x) { return x+x; } } _add add;

Did you mean: struct _add { typeof(A.init+B.init) opCall(A, B)(A a, B b) { return a + b; } } _add add; auto x = add(3, 0.1415926); ?
 Now you can give it to a function, without the need of specializing it  
 for a type T first (you can't give the pointer to a template). (This may  
 also be faster, because there isn't a delegate to call).
 (Haskell is statically typed and allows you do more complex things with  
 a clean enough syntax).

Yes, it is a nice trick.
Nov 25 2008