November 12, 2009
Hi Nick,

Thanks for the response.  More below.

On Mon, Nov 9, 2009 at 6:46 PM, Nick Sabalausky <a@a.a> wrote:
> "Bill Baxter" <wbaxter@gmail.com> wrote in message news:mailman.290.1257812868.20261.digitalmars-d-announce@puremagic.com...
>> On Mon, Nov 9, 2009 at 4:09 PM, Walter Bright <newshound1@digitalmars.com> wrote:
> I have a few things, mostly part of my SemiTwist D Tools project (http://www.dsource.org/projects/semitwist/):
>
> === Trace / Trace Value ===
>
> Two of my personal favorites (particularly traceVal). Great for debugging.
>
> Implementation and Documentation (search for "trace" and "traceVal", implementations are less than 10 lines each): http://www.dsource.org/projects/semitwist/browser/trunk/src/semitwist/util/mixins.d
>
> Quick Example:
> -----------------------------
> mixin(trace!());
> mixin(trace!("--EASY TO VISUALLY GREP--"));
>
> int myVar=100;
> mixin(traceVal!("myVar  "));
> mixin(traceVal!("myVar-1"));
> mixin(traceVal!("min(4,7)", "max(4,7)"));
> -----------------------------
>
> Output:
> -----------------------------
> C:\path\file.d(1): trace
> --EASY TO VISUALLY GREP--: C:\path\file.d(3): trace
> myVar  : 100
> myVar-1: 99
> min(4,7): 4
> max(4,7): 7
> -----------------------------

This does, look handy, but the C++ equivalent based on preprocessor
macros arguably looks better and has a simpler implementation:
    #define traceVal(x) cout << #x " : " <<  x; << endl;
and to use just:
    traceVal(myVar);

(assuming for a moment that c++ had a writefln)

However, the unescape() function used in traceVal looks like it might be a useful CTFE example.  Some kind of compile-time string escaping/unescaping could definitely be a good example.

> There's also a traceMixin that can be useful for debugging mixins (it'll
> replace a normal string mixin and echo at compile time via "pragma(msg, )"
> the string being mixed in).
>
> === Init Member ===
>
> Great for DRY in constructors with many initialization parameters.
>
> Implementation and Documentation (at the top of the file): http://www.dsource.org/projects/semitwist/browser/trunk/src/semitwist/util/mixins.d
>
> Quick Example:
> -----------------------------
> mixin(initMember!(someVar));
> mixin(initMember!(a, b, c));
> -----------------------------
>
> Turns Into:
> -----------------------------
> this.someVar = someVar;
> this.a = a;
> this.b = b;
> this.c = c;
> -----------------------------
>
> Some variations are also available, such as initMemberFrom for copy constructors and initFrom copying class members to local vars.

I think I'd rather focus on examples that let you do something that would be difficult to do otherwise, rather than something that saves a bit of typing.

> === Getters ===
>
> DRY mixins for publicly read-only properties, and a "lazy" version for lazily computed & cached read-only properties. A poor replacement for a real DRY property syntax, but the next best thing.
>
> Implementation and Documentation (search for "getter" and "getterLazy"): http://www.dsource.org/projects/semitwist/browser/trunk/src/semitwist/util/mixins.d
>
> Quick Example:
> -----------------------------
> // Third param optional
> mixin(getter!(float, "someFloat", 2.5));
>
> mixin(getterLazy!(int, "myVar"));
> private int _myVar_gen()
> {
>    // Ordinarily, this function would be much more complex
>    // Also, any member func can set "_myVar_cached = false;"
>    // to force this to be re-computed on the next request.
>    return 7;
> }
> -----------------------------
>
>
> Turns Into:
> -----------------------------
> private float _someFloat = 2.5;
> private float someFloat(float _NEW_VAL_)
> {
>    _someFloat = _NEW_VAL_;
>    return _someFloat;
> }
> public float someFloat()
> {
>    return _someFloat;
> }
>
> private int _myVar;
> private bool _myVar_cached = false;
> public int myVar() {
>    if(!_myVar_cached) {
>        _myVar_cached = true;
>        _myVar = _myVar_gen();
>    }
>    return _myVar;
> }
> private int _myVar_gen()
> {
>    // Ordinarily, this function would be much more complex
>    // Also, any member func can set "_myVar_cached = false;"
>    // to force this to be re-computed on the next request.
>    return 7;
> }
> -----------------------------
>
> Variations are also available to use "protected" (or anything else) instead
> of private.

This is more interesting than the last, but still falls pretty much in the "saves typing" category.  Also I think it's arguable that these sacrifice some readablility to gain their terseness.  For this sort of thing I'd rather have an IDE save me the typing, but actually create the code, instead of resorting to semi-cryptic naming conventions.

> === Defer Assert/Ensure ===
>
> Uses metaprogramming to create an alternative to assert() that provides much
> of the usefulness of JUnit-style libs, but without the bulk of wrapping
> things like '==' and '||' in classes/structs or forcing unnatural syntax
> like '(a.equals(b)).or(c.notEquals(d))'. Also reports unexpected exceptions
> and allows requiring a particular exception to be thrown. Implemented in
> less than 200 lines, including blank lines and comments.
>
> Implementation: http://www.dsource.org/projects/semitwist/browser/trunk/src/semitwist/util/deferAssert.d
>
> Simple Test App: http://www.dsource.org/projects/semitwist/browser/trunk/src/semitwist/apps/tests/deferAssertTest/main.d
>
> Quick Example:
> -----------------------------
> int foo = 2;
> mixin(deferAssert!(`foo == 3 || foo > 5`, "foo is bad"));
> mixin(deferEnsure!(`foo`, `_ == 3 || _ > 5`, "ensure foo failed"));
> flushAsserts();
> -----------------------------
>
> Output:
> -----------------------------
> src\semitwist\apps\tests\deferAssertTest\main.d(37): Assert Failed (foo == 3
> || foo > 5): foo is bad
> src\semitwist\apps\tests\deferAssertTest\main.d(42): Ensure Failed: ensure
> foo failed
> Expression 'foo':
> Expected: _ == 3 || _ > 5
> Actual: 2
> tango.core.Exception.AssertException@src\semitwist\util\deferAssert.d(170):
> 2 Assert Failures
> -----------------------------


This is another one that C++ people would just throw macros at and wonder what the big deal is.


> === Compile-time checking on types with non-identifier strings ===
>
> Another of my favorites. This is from an upcoming release of my Goldie ( http://www.dsource.org/projects/goldie ) parser library.
>
> I have a class to represent a token, which could have any name like "+" or "Statement", etc., all depending on the user-defined grammar. Currently, this token name representation is dynamic/run-time, which is very flexible, but there's no compile-time checking of these names, which often a bad tradeoff because many programs that need to parse something already know (in fact, *must* already know) the token names, so the flexibility doesn't help them at all, but compile-time checking would.
>
> Unfortunately, the traditional approach to adding static checking would require every token name to be a valid identifier name, so for instance, "+" would have to be changed to "Plus", etc. And this can be problematic. It's extra work. It's very difficult to automate while getting good well-named results. It's possible (in a company setting, for instance) that the programmer might not be allowed or able to change the grammar. And of course, there's the possibility for name collisions (ie, what if "Plus" was already taken by another token?). Also, the new static class definition would probably need to be duplicated for each token. But all those problems can be solved in D quite easily with just a very small amount of some very simple metaprogramming.
>
> The actual implementation Goldie uses does get fairly involved, but the basic concept is dead simple. All it takes is one templated type paramater, one static if and one static assert (and the static if could easily be eliminated):
>
> -------------------------------------------------
> // ----------- Dynamic Token -----------
>
> // A Dynamic token. Flexible, but no compile-time checking
> class DynamicToken
> {
>    protected char[] name;
>    this(char[] name)
>    {
>        if(name != "+" && name != "Statement")
>            throw new Exception("Invalid token: " ~ name);
>
>        this.name = name;
>    }
>    // More Tokeny stuff here
> }
> unittest
> {
>    auto t1 = new DynamicToken("+");
>    auto t2 = new DynamicToken("Statement");
>    auto t3 = new DynamicToken("Stment"); // Runtime error
> }
>
> // ----------- Old-Style Static Token -----------
>
> class OldStyle_StaticToken_Statement : DynamicToken
> {
>    this() { super("Statement"); }
> }
> // Note that "+" must be changed to "Plus",
> // Also, the whole class definition has to be repeated
> // (Although it's pretty small in this case.)
> class OldStyle_StaticToken_Plus : DynamicToken
> {
>    this() { super("+"); }
> }
> unittest
> {
>    // Supposed to be +, but had to be changed to Plus
>    // by either the grammar author or by special complex
>    // logic in the StaticToken-generation tool.
>    // And if there was already another token actually named "Plus"...
>    // Well, even more complexity arises.
>
>    auto t1 = new OldStyle_StaticToken_Plus;
>    auto t2 = new OldStyle_StaticToken_Statement;
>    //auto t3 = new OldStyle_StaticToken_Stment; // Compile-time error
> }
>
> // ----------- New-Style Static Token -----------
>
> class NewStyle_StaticToken(char[] staticName) : DynamicToken
> {
>    static if(staticName != "+" && staticName != "Statement")
>        static assert(false, "Invalid token: " ~ staticName);
>
>    this() { super(staticName); }
> }
> unittest
> {
>    auto t1 = new NewStyle_StaticToken!("+");
>    auto t2 = new NewStyle_StaticToken!("Statement");
>    //auto t3 = new NewStyle_StaticToken!("Stment"); // Compile-time error
> }
> -------------------------------------------------

That is kinda neat, I suppose you could even extend that to make the list of valid tokens be a template parameter (either variadic, or separated by some delimiter in one big string that then gets broken up at runtime).  I guess you could pitch this as a baby step towards the full-fledged compile-time parser.  Step 0 recognize valid tokens.

Thanks for writing all these up.

--bb
November 12, 2009
2009/11/10 Denis Koroskin <2korden@gmail.com>:
> On Tue, 10 Nov 2009 03:27:20 +0300, Bill Baxter <wbaxter@gmail.com> wrote:
>
> kprintf (a printf variant) in XoMB is a nice example imo. It accepts format
> as a template parameter, doing all the checks at compile-time (and more).
>
> http://github.com/xomboverlord/xomb-bare-bones/blob/6d924a9fd7cafe43aa50f38c0cd04c44187d4993/kernel/core/kprintf.d
>

Nice.  400 lines is a little long for this purpose, though.  Maybe a somewhat simplified version that just replaces {} with template args would be good.  I guess that's pretty much what std.metastrings.Format does, just with %s instead of {}.

--bb
November 12, 2009
On 11/11/09 17:58, grauzone wrote:
> Andrei Alexandrescu wrote:
>> grauzone wrote:
>>> Don wrote:
>>>> Christopher Wright wrote:
>>>>> grauzone wrote:
>>>>>> You're not testing for types, you're testing if it compiles.
>>>>>> Inside the tested block of code, all sorts of things could go
>>>>>> wrong. You can't know if is(typeof(...)) really did what you
>>>>>> wanted, or if something broke.
>>>>
>>>> You're testing, "is everything inside that OK?". If you want to know
>>>> WHY it's wrong, you'd better make sure you're testing something simple.
>>>
>>> Andrei's range lib uses it more in a way "does this type support this
>>> and that range interface?". Example:
>>> http://dsource.org/projects/phobos/browser/trunk/phobos/std/range.d#L58
>>
>> Then different isXxxRange are used by higher-order ranges in defining
>> refined interfaces depending on the interfaces offered by their inputs.
>
> That means if one isXxxRange fails because the user maybe made a typo in
> the needed range function, the code will silently do something else.
>
> But my main problem is that the user just gets a "does not match
> template declaration" compiler error when he messes up his range
> interface. He's neither told that e.g. his range-related function
> returns the wrong type, nor is there any other refined error message.

The check doesn't have to look like that. The check can be inside the template with static if and you can have several checks for different things and use static assert to output a error message that make sense.

> Now what if we'd introduce some sort of interfaces for type checking at
> compile time?
>
> interface InputRange(T) {
> void popFront();
> bool empty();
> T front();
> }
>
> struct MyRange : InputRange!(int) {
> void popFront() { ... }
>
> //compiler error goes here...
> void empty() { ... }
>
> int front() { ... }
> }
>
> (or something like this)
>
> PS: there are two aspects to the problem: 1. even compile time duck
> typing shares some of the problems of runtime duck typing, and 2.
> utterly unhelpful error messages. If you wouldn't explicitly check the
> interface with is(typeof()), the compiler's error messages would be even
> worse because of 1.
>
>> I fail to see how that's terrible. I am very happy D has that feature
>> - no other statically-typed language has it, and it can be used to
>> great effect. Look e.g. at Chain:
>>
>> http://dsource.org/projects/phobos/browser/trunk/phobos/std/range.d#L799
>>
>> There, the uses of static if (is(...)) allow Chain to define as
>> capable an interface as its inputs allow.
>>
>>
>> Andrei

November 12, 2009
>"Bill Baxter" <wbaxter@gmail.com> wrote in message news:mailman.337.1258023453.20261.digitalmars-d-announce@puremagic.com... Hi Nick,
>
>Thanks for the response.  More below.
>
>However, the unescape() function used in traceVal looks like it might be a useful CTFE example.  Some kind of compile-time string escaping/unescaping could definitely be a good example.

Heh, I tend to forget about that one. That's from the "semitwist.util.text" module, which I haven't worked with much in a long time, so there's probably a lot of WTFs spread through the module, and if you use something there as an example, you might need to clean it up a little. FWIW, that double-quote-string escaping/unescaping is built around ctfe_substitute which is in "semitwist.util.ctfe" (although Tango might have something similar by now).

>> [various]
>
>This is another one that C++ people would just throw macros at and wonder what the big deal is.

Ah, yea, good point. Of course, we'd probably all take D's metaprogramming over preprocessor macros anyday, but I can definitely see their point.

>> === Compile-time checking on types with non-identifier strings ===
>
>That is kinda neat, I suppose you could even extend that to make the list of valid tokens be a template parameter (either variadic, or separated by some delimiter in one big string that then gets broken up at runtime).  I guess you could pitch this as a baby step towards the full-fledged compile-time parser.  Step 0 recognize valid tokens.
>

Interesting idea, yea. Might not be much help for the lib as I currently have it though, since I'm just spitting out the static-style language definitions from a tool that takes in a grammar and generates D code. Certainly something to keep in mind for future expansion though.

>Thanks for writing all these up.

No prob! :)


November 12, 2009
"Nick Sabalausky" <a@a.a> wrote in message news:hdgt9q$vti$1@digitalmars.com...
> >"Bill Baxter" <wbaxter@gmail.com> wrote in message news:mailman.337.1258023453.20261.digitalmars-d-announce@puremagic.com...
>>Hi Nick,
>
>>> === Compile-time checking on types with non-identifier strings ===
>>
>>That is kinda neat, I suppose you could even extend that to make the list of valid tokens be a template parameter (either variadic, or separated by some delimiter in one big string that then gets broken up at runtime).  I guess you could pitch this as a baby step towards the full-fledged compile-time parser.  Step 0 recognize valid tokens.
>>
>
> Interesting idea, yea. Might not be much help for the lib as I currently have it though, since I'm just spitting out the static-style language definitions from a tool that takes in a grammar and generates D code. Certainly something to keep in mind for future expansion though.
>

One other idea I had for expanding on that NewStyle_StaticToken would be: Suppose a grammar had some sort of naming convention that, well, meant something within the context of that grammar. Then the application code could take something like "process(Token!("foo") token) {...}" and use compile-time string processing to do advanced pattern-matching on the token-name when selecting which token types to process a certain way (maybe it would have to be a static if or a templated instead of a basic function though). Not a fully fleshed-out idea, of course, but maybe the start of something. In fact, I think tango.util.Convert does something vaguely like that to look for conversion functions.


November 12, 2009
On Tue, Nov 10, 2009 at 3:32 AM, Lutger <lutger.blijdestijn@gmail.com> wrote:
> Bill Baxter wrote:
>
>> On Mon, Nov 9, 2009 at 4:09 PM, Walter Bright <newshound1@digitalmars.com> wrote:
>>> Looks like Bill Baxter is giving a presentation on D Nov. 18!
>>>
>>> http://www.nwcpp.org/
>>
>> Yep, that's right, and I'd be quite grateful to you smart folks here if you could share your meta-programming favorites with me!   If you've got a real-world example of meta-programming in D that you think is particularly handy, then please send it my way
>>
>> I'm looking for small-but-useful things that are easy to explain, and make something easier than it would be otherwise.  Things like places where static if can save your butt,  or loop unrolling,  and passing code snippets to functions like in std.algorithm.
>>
>> Things like a compile-time raytracer or regexp parser (though quite cool!) are not what I'm after.  Too involved for a short talk.
>>
>> --bb
>
> Great, I hope to see some slides or a video!
>
> Here is a watered down example of what I currently use to help with the boilerplate code of a visitor. It doesn't look much, but it's helpful when you have lots of visitable classes and in case of adding new classes. The code should run as is:
>
> import std.stdio;
>
> enum Visitable =
> q{
>    override void accept(IVisitor visitor)
>    {
>        visitor.visit(this);
>    }
> };
>
> interface IExpression
> {
>    void accept(IVisitor visitor);
> }
>
> class Sum : IExpression
> {
>    mixin(Visitable);
> }
>
> class Product : IExpression
> {
>    mixin(Visitable);
> }
>
> string visitMethodOf(string type)
> {
>    return "void visit(" ~ type ~ ");";
> }
>
> string FoldStringsOf(alias F)(string[] list)
> {
>    string result = "";
>    foreach (element ; list)
>        result ~= F(element);
>    return result;
> }
>
> interface IVisitor
> {
>    mixin( FoldStringsOf!visitMethodOf( ["Sum", "Product"] ) );
> }
>
> class Visitor : IVisitor
> {
>    void visit(Sum node)
>    {
>        writeln("sum");
>    }
>
>    void visit(Product node)
>    {
>        writeln("product");
>    }
> }
>
> void main()
> {
>    auto printer = new Visitor();
>
>    IExpression sum = new Sum();
>    IExpression product = new Product();
>
>    sum.accept(printer);
>    product.accept(printer);
> }
>

This is almost just a preprocessor macro trick, except for this line:
     mixin( FoldStringsOf!visitMethodOf( ["Sum", "Product"] ) );

The essence is to generate a bunch of methods from a list of names.  I was thinking to include a similar example from the world of 3d graphics, which is generating all the swizzles of a vector* (got the idea from Tom S.).  That's got the method generation from a list, but it also generates the list too.

Another way is to have a swizz member template that lets you do v.swiz("zyx").  That's arguably better in that you don't pay for it if you don't use it.  But being able to generate all the methods with D is still kinda spiffy.

* Most GPU languages (HLSL,glsl,Cg) have a swizzle syntax, such that
for a float3 v;   v.zyx  gives you float3(v.z,v.y,v.x).  And this
works for all suffixes.   v.xxx, v.xzy, etc.  Also for different
numbers of components.   eg.  v.xz is float2(v.x,v.z).

--bb
November 12, 2009
On Wed, Nov 11, 2009 at 8:23 AM, grauzone <none@example.net> wrote:
> Don wrote:
>>
>> Christopher Wright wrote:
>>>
>>> grauzone wrote:
>>>>
>>>> You're not testing for types, you're testing if it compiles. Inside the tested block of code, all sorts of things could go wrong. You can't know if is(typeof(...)) really did what you wanted, or if something broke.
>>
>> You're testing, "is everything inside that OK?". If you want to know WHY it's wrong, you'd better make sure you're testing something simple.
>
> Andrei's range lib uses it more in a way "does this type support this and that range interface?". Example: http://dsource.org/projects/phobos/browser/trunk/phobos/std/range.d#L58

But notice he also has unit tests for it that check some things which
are supposed to pass and not pass.
So if the is(typeof()) were failing because of a typo, he'd know it.
I think the problem with an interface-like specification is that things like
   if (r.empty) {}
are hard to specify other than by asking if the code compiles.  The
algo doesn't actually require an r.empty that returns bool.  It
requires an r.empty that can can be used in a conditional.  It's
actually ok if r.empty returns null on empty and non-null otherwise.
"Does this code compile" is actually the right question to ask, IMHO.

But I know what you mean.  I just don't know if it's possible to do
better.  Actually it already errors out if you give it structurally
invalid code.   For instance all these generate errors:
    static if(is(typeof{ int x   if (x) {} }))
    static if(is(typeof{ int x; iff (x) {} }))
    static if(is(typeof{ int x; if (x)) {} }))
    static if(is(typeof{ int x; for (;) {} }))

So I think the only things that give you errors are ones where you've
typed an identifier wrong, or asked for function call when you meant
indexing etc.   Given that, I'm not sure how creating interfaces would
make anything different.  You could still have typos in your interface
declaration.  I think it ends up being pretty much equivalent to
defining a "template supportsInterface(T)".   Having a "myType :
implements static Interface" syntax is the moral equivalent of "static
assert(supportsInterface!(myType))".
The only difference is that a static interface syntax might be able to
give you better error messages than "assertion failed".

Ok, so I guess that's something that would be valuable:   We need some
way to get the reason for failure from static if (is(typeof(...)).

If you had that, then I think you could write a checkInterface!(T)
that reports decent error messages.
I think I'll start another thread about that.  d.announce clearly
isn't the right place for this discussion.

----------
Somewhat related -- this topic ties into something I brought up
before.  I often find I end up writing bits of code twice:
     static if (is(typeof({ a[x] += c; }))) {
            a[x] += c;
     }
     else {
             // fallback
     }

It feels like there should be a construct that compiles some code if
it can, otherwise do something else.
Maybe:

static if {
    a[x] += c;
} else {
    // fallback
}


--bb
November 12, 2009
On Thu, 12 Nov 2009 16:01:20 +0300, Bill Baxter <wbaxter@gmail.com> wrote:

> On Wed, Nov 11, 2009 at 8:23 AM, grauzone <none@example.net> wrote:
>> Don wrote:
>>>
>>> Christopher Wright wrote:
>>>>
>>>> grauzone wrote:
>>>>>
>>>>> You're not testing for types, you're testing if it compiles. Inside the
>>>>> tested block of code, all sorts of things could go wrong. You can't know if
>>>>> is(typeof(...)) really did what you wanted, or if something broke.
>>>
>>> You're testing, "is everything inside that OK?". If you want to know WHY
>>> it's wrong, you'd better make sure you're testing something simple.
>>
>> Andrei's range lib uses it more in a way "does this type support this and
>> that range interface?". Example:
>> http://dsource.org/projects/phobos/browser/trunk/phobos/std/range.d#L58
>
> But notice he also has unit tests for it that check some things which
> are supposed to pass and not pass.
> So if the is(typeof()) were failing because of a typo, he'd know it.
> I think the problem with an interface-like specification is that things like
>    if (r.empty) {}
> are hard to specify other than by asking if the code compiles.  The
> algo doesn't actually require an r.empty that returns bool.  It
> requires an r.empty that can can be used in a conditional.  It's
> actually ok if r.empty returns null on empty and non-null otherwise.
> "Does this code compile" is actually the right question to ask, IMHO.
>
> But I know what you mean.  I just don't know if it's possible to do
> better.  Actually it already errors out if you give it structurally
> invalid code.   For instance all these generate errors:
>     static if(is(typeof{ int x   if (x) {} }))
>     static if(is(typeof{ int x; iff (x) {} }))
>     static if(is(typeof{ int x; if (x)) {} }))
>     static if(is(typeof{ int x; for (;) {} }))
>
> So I think the only things that give you errors are ones where you've
> typed an identifier wrong, or asked for function call when you meant
> indexing etc.   Given that, I'm not sure how creating interfaces would
> make anything different.  You could still have typos in your interface
> declaration.  I think it ends up being pretty much equivalent to
> defining a "template supportsInterface(T)".   Having a "myType :
> implements static Interface" syntax is the moral equivalent of "static
> assert(supportsInterface!(myType))".
> The only difference is that a static interface syntax might be able to
> give you better error messages than "assertion failed".
>
> Ok, so I guess that's something that would be valuable:   We need some
> way to get the reason for failure from static if (is(typeof(...)).
>
> If you had that, then I think you could write a checkInterface!(T)
> that reports decent error messages.
> I think I'll start another thread about that.  d.announce clearly
> isn't the right place for this discussion.
>
> ----------
> Somewhat related -- this topic ties into something I brought up
> before.  I often find I end up writing bits of code twice:
>      static if (is(typeof({ a[x] += c; }))) {
>             a[x] += c;
>      }
>      else {
>              // fallback
>      }
>
> It feels like there should be a construct that compiles some code if
> it can, otherwise do something else.
> Maybe:
>
> static if {
>     a[x] += c;
> } else {
>     // fallback
> }
>
>
> --bb

string expression = "a[x] += c";
static if (meta.compiles(expression)) {
    mixin(expression);
} else {
    // fallback
}

or even like this:

expr expression = "a[x] += c";
static if (expression.compiles) {
    expression(); // or expression.evaluate();
} else {
    // fallback
}
November 12, 2009
Andrei Alexandrescu wrote:
> grauzone wrote:
>> Don wrote:
>>> Christopher Wright wrote:
>>>> grauzone wrote:
>>>>> You're not testing for types, you're testing if it compiles. Inside the tested block of code, all sorts of things could go wrong. You can't know if is(typeof(...)) really did what you wanted, or if something broke.
>>>
>>> You're testing, "is everything inside that OK?". If you want to know WHY it's wrong, you'd better make sure you're testing something simple.
>>
>> Andrei's range lib uses it more in a way "does this type support this and that range interface?". Example: http://dsource.org/projects/phobos/browser/trunk/phobos/std/range.d#L58
> 
> Then different isXxxRange are used by higher-order ranges in defining refined interfaces depending on the interfaces offered by their inputs. I fail to see how that's terrible. I am very happy D has that feature - no other statically-typed language has it, and it can be used to great effect. Look e.g. at Chain:
> 
> http://dsource.org/projects/phobos/browser/trunk/phobos/std/range.d#L799
> 
> There, the uses of static if (is(...)) allow Chain to define as capable an interface as its inputs allow.
> 
> 
> Andrei

I really like Andrei's range library, I use it all the time when I need to pass slices of generic types around, it really is more convenient than a pair of iterators.

The way I see ranges is as a form of interface without being bound to classes; its the only way to make structs and D arrays pass the isXxxRange traits.

The prototypes for using ranges then just go from:

void dosomething(T)(IForwardRange!T range) {}

to

void dosomething(Range)(Range range) if(isForwardRange!Range) {}

and it means the same thing, but you can't send a string to IForwardRange.
November 12, 2009
Bill Baxter wrote:
> On Mon, Nov 9, 2009 at 6:03 PM, Andrei Alexandrescu
> <SeeWebsiteForEmail@erdani.org> wrote:
>> Bill Baxter wrote:
>>> On Mon, Nov 9, 2009 at 4:09 PM, Walter Bright
>>> <newshound1@digitalmars.com> wrote:
>>>> Looks like Bill Baxter is giving a presentation on D Nov. 18!
>>>>
>>>> http://www.nwcpp.org/
>>> Yep, that's right, and I'd be quite grateful to you smart folks here
>>> if you could share your meta-programming favorites with me!   If
>>> you've got a real-world example of meta-programming in D that you
>>> think is particularly handy, then please send it my way
>>>
>>> I'm looking for small-but-useful things that are easy to explain, and
>>> make something easier than it would be otherwise.  Things like places
>>> where static if can save your butt,  or loop unrolling,  and passing
>>> code snippets to functions like in std.algorithm.
>>>
>>> Things like a compile-time raytracer or regexp parser (though quite
>>> cool!) are not what I'm after.  Too involved for a short talk.
>>>
>>> --bb
>> std.random has code that checks the parameters of a congruential RNG during
>> compilation. That's also an example in TDPL.
> 
> This looks good.
> Any chance you could send me the snippet of the book that explains the
> rationale for what constitutes "proper linear congruential
> parameters"?
> 
> --bb

It's been online for a while.

http://erdani.com/tdpl/excerpt.pdf


Andrei