Thread overview
Compile time metaprogramming
Dec 02, 2011
simendsjo
Dec 02, 2011
Jonathan M Davis
Dec 03, 2011
simendsjo
Dec 03, 2011
David Nadlinger
Dec 03, 2011
Dejan Lekic
Dec 03, 2011
simendsjo
Dec 03, 2011
David Nadlinger
Dec 04, 2011
Simen Kjærås
December 02, 2011
I had a little talk with one of my teachers regarding the culprits of wrong unit assumptions in code (http://mars.jpl.nasa.gov/msp98/news/mco990930.html).

We had a little different views on how much pain it would be for developers to code using an SI library, so I hacked together a small proof-of-concept in D to show how D's metaprogramming could make things a lot less painful than, say, Java.

I thought I could share it here too even though it's just a small hack with Norwegian comments.


import std.traits, std.stdio, std.conv;

void main() {
    auto f = feet(10);
    auto m = meter(10.0); // kan fint bruke desimaltall

    // en type til en annen - konvertering
    // kan gjerne bruke /, * e.l. også - metaprogrammering er en god ting :)
    auto feetMeter = f + m;
    assert( feetMeter.siunit == "feet" );
    assert( feetMeter >= feet(42.808) &&
            feetMeter <= feet(42.809) );

    auto meterFeet = m + f;
    assert( meterFeet.siunit == "meter" );
    assert( meterFeet >= meter(13.0475) &&
            meterFeet <= meter(13.0485) );

    // kan også bruke skalarer - og fint mikse floats inn i det
    assert( m * 2.2 == meter(22) );

    // eller konvertere direkte
    auto f2 = meterFeet.to!"feet"();
    assert( f2.siunit == "feet" );
    assert( f2 >= feet(42.808) &&
            f2 <= feet(42.809) );

    // men kan ikke konvertere uten konverteringsfunksjoner
    static assert(!__traits(compiles, m.to!"finnes ikke"()) );

    // men kan ikke manipulere urelaterte typer
    static assert(!__traits(compiles, feet(10) + celsius(10)) );

    // kan caste til kompatible typer
    double d = cast(double)m;
    assert( d == 10.0 );

    // eller sammenlikne direkte
    assert( m == 10 );
    assert( m > 9 );
    assert( m < 11 );
}

auto meter(V)(V value) pure nothrow @safe {
    return Value!("meter", V)(value);
}

auto feet(V)(V value) pure nothrow @safe {
    return Value!("feet", V)(value);
}

auto celsius(V)(V value) pure nothrow @safe {
    return Value!("celsius", V)(value);
}

auto fahrenheit(V)(V value) pure nothrow @safe {
    return Value!("fahrenheit", V)(value);
}

auto SIConvert(string S, string D, T)(T value) pure nothrow @safe
if(S == "meter" && D == "feet") {
    return value / 0.3048;
}

auto SIConvert(string S, string D, T)(T value) pure nothrow @safe
if(S == "feet" && D == "meter") {
    return value * 0.3048;
}

auto SIConvert(string S, string D, T)(T value) pure nothrow @safe
if(S == "celsius" && D == "fahrenheit") {
    return (value - 32) * (5/9);
}

auto SIConvert(string S, string D, T)(T value) pure nothrow @safe
if(S == "fahrenheit" && D == "celsius") {
    return value * (9/5) + 32;
}

template isSIValue(T) {
    // HACK: Sjekker kun om det har en unit verdi...
    static if( is(typeof(T.siunit) : string) )
        enum isSIValue = true;
    else
        enum isSIValue = false;
}

auto SIConvert(S, D, T)(T value) pure nothrow @safe
if(isSIValue!S && isSIValue!D) {
    static if( S.siunit == D.siunit )
        return value;
    else {
        static assert(__traits(compiles, SIConvert!(S.siunit, D.siunit, T)(value)),
                    "Cannot convert value of type "~T.stringof~
                    " from "~S.siunit~" to "~D.siunit);
        return SIConvert!(S.siunit, D.siunit, T)(value);
    }
}


struct Value(string SIUnit, V) if(isNumeric!V) {
    enum siunit = SIUnit; // finnes kun ved kompilering
    immutable V value;

    this(V)(V value) pure nothrow @safe {
        this.value = value;
    }

    auto opBinary(string op, RHS)(RHS r) const nothrow pure @safe
    if(isSIValue!RHS) {
        auto converted = SIConvert!(RHS, typeof(this))(r.value);
        mixin("auto newval = value "~op~" converted;");
        return Value!(siunit, typeof(newval))(newval);
    }

    auto opBinary(string op, RHS)(const RHS r) const nothrow pure @safe
    if(isNumeric!RHS) {
        mixin("auto val = value "~op~" r;");
        return Value!(siunit, typeof(val))(val);
    }

    auto to(string unit)() {
        auto val = SIConvert!(siunit, unit, typeof(value))(value);
        return Value!(unit, typeof(val))(val);
    }

    T opCast(T)() const pure nothrow @safe
    if(is(typeof(value) : T)) {
        return cast(T)value;
    }

    bool opEquals(T)(const T o) const pure nothrow @safe
    if(isSIValue!T) {
        return o.siunit == siunit && o.value == value;
    }

    bool opEquals(T)(const T o) const pure nothrow @safe
    if(is(T : typeof(value))) {
        return value == o;
    }

    int opCmp(T)(const T v) const pure nothrow @safe
    if(isSIValue!T) {
        static assert(T.siunit == siunit, "Cannot compare "~siunit~" to "~T.siunit~
                ". Try converting the value first");
        return value < v ? -1 : (value > v ? 1 : 0);
    }

    int opCmp(T)(const T o) const pure nothrow @safe
    if(isNumeric!T) {
        return value < o ? -1 : (value > o ? 1 : 0);
    }

    string toString() const @trusted {
        return .to!string(value)~" "~siunit;
    }
}

December 02, 2011
On Friday, December 02, 2011 22:00:44 simendsjo wrote:
> I had a little talk with one of my teachers regarding the culprits of wrong unit assumptions in code (http://mars.jpl.nasa.gov/msp98/news/mco990930.html).
> 
> We had a little different views on how much pain it would be for developers to code using an SI library, so I hacked together a small proof-of-concept in D to show how D's metaprogramming could make things a lot less painful than, say, Java.
> 
> I thought I could share it here too even though it's just a small hack with Norwegian comments.

A unit library is definitely a great place for taking advantage of templates - that's what core.time and std.datetime do with time units (e.g. dur!"seconds"(5)). There's also been at least a couple of cases where people have worked on unit libraries and discussed them in the main newsgroup, but so far, nothing has gotten to the point where it's been reviewed for introduction to Phobos, and I don't know if any of those projects is still alive. It would definitely be an asset though.

- Jonathan M Davis
December 03, 2011
On 02.12.2011 23:28, Jonathan M Davis wrote:
> There's also been at least a couple of cases where people
> have worked on unit libraries and discussed them in the main newsgroup, but so
> far, nothing has gotten to the point where it's been reviewed for introduction
> to Phobos, and I don't know if any of those projects is still alive. It would
> definitely be an asset though.

I seem to remember someone wanting to work on a library for GSoC and that he would work on it even if it wasn't accepted. I might remember wrong, and the person might have left the project.
December 03, 2011
On 12/3/11 1:43 AM, simendsjo wrote:
> On 02.12.2011 23:28, Jonathan M Davis wrote:
>> There's also been at least a couple of cases where people
>> have worked on unit libraries and discussed them in the main
>> newsgroup, but so
>> far, nothing has gotten to the point where it's been reviewed for
>> introduction
>> to Phobos, and I don't know if any of those projects is still alive.
>> It would
>> definitely be an asset though.
>
> I seem to remember someone wanting to work on a library for GSoC and
> that he would work on it even if it wasn't accepted. I might remember
> wrong, and the person might have left the project.

That was Cristi Cobzarenco, but he then proposed a different project, linear algebra stuff [1], which he ended up working on because I already had a more or less working implementation of an units library lying around: [2].

I posted my project to the NG, and there seemed to actually be two or three people interested in it, but I didn't submit it to formal review yet, because it sometimes breaks in interesting ways due to compiler bugs (issue 3467 [3] and the likes), and I had enough work to do for my own GSoC project anyway.

David


[1] http://www.google-melange.com/gsoc/project/google/gsoc2011/cristicbz/36001
[2] http://klickverbot.at/code/units/
[3] http://d.puremagic.com/issues/show_bug.cgi?id=3467
December 03, 2011
David, to be frank, your code is already useful! Something is better than *nothing*! I hope you or someone else will continue with these two modules, and include them in Phobos.
December 03, 2011
On 03.12.2011 10:26, David Nadlinger wrote:
>
> I posted my project to the NG, and there seemed to actually be two or
> three people interested in it, but I didn't submit it to formal review
> yet, because it sometimes breaks in interesting ways due to compiler
> bugs (issue 3467 [3] and the likes), and I had enough work to do for my
> own GSoC project anyway.

Seems one of your bugs recently got a pull request:
https://github.com/D-Programming-Language/dmd/pull/449

Gotta love the move to github!
December 03, 2011
On 12/3/11 1:49 PM, simendsjo wrote:
> Seems one of your bugs recently got a pull request:
> https://github.com/D-Programming-Language/dmd/pull/449

I'm aware of that, though Walter apparently still thinks it's okay for foo!3u and foo!3 to be different things given »template foo(uint u)«…

David
December 04, 2011
On Sat, 03 Dec 2011 10:26:09 +0100, David Nadlinger <see@klickverbot.at> wrote:

> On 12/3/11 1:43 AM, simendsjo wrote:
>> On 02.12.2011 23:28, Jonathan M Davis wrote:
>>> There's also been at least a couple of cases where people
>>> have worked on unit libraries and discussed them in the main
>>> newsgroup, but so
>>> far, nothing has gotten to the point where it's been reviewed for
>>> introduction
>>> to Phobos, and I don't know if any of those projects is still alive.
>>> It would
>>> definitely be an asset though.
>>
>> I seem to remember someone wanting to work on a library for GSoC and
>> that he would work on it even if it wasn't accepted. I might remember
>> wrong, and the person might have left the project.
>
> That was Cristi Cobzarenco, but he then proposed a different project, linear algebra stuff [1], which he ended up working on because I already had a more or less working implementation of an units library lying around: [2].
>
> I posted my project to the NG, and there seemed to actually be two or three people interested in it, but I didn't submit it to formal review yet, because it sometimes breaks in interesting ways due to compiler bugs (issue 3467 [3] and the likes), and I had enough work to do for my own GSoC project anyway.

I, for one, wholeheartedly support the inclusion of this in Phobos.

Oh, and Walter's reply to #3467 is scary. I hope he comes around to
seeing he's not making sense.