Thread overview
Units of Measurement Library: units-d
Mar 31, 2016
Nordlöw
Apr 01, 2016
ag0aep6g
Apr 01, 2016
Simen Kjaeraas
Apr 01, 2016
ag0aep6g
Apr 01, 2016
Simen Kjaeraas
Apr 02, 2016
Meta
Apr 04, 2016
Simen Kjaeraas
Apr 02, 2016
crimaniak
Apr 01, 2016
Jack Stouffer
March 31, 2016
I've put David Nadlinger work together with my tweaks on top at

https://github.com/nordlow/units-d

to make it easier to experiment with.

PR are very welcome.
April 01, 2016
On 01.04.2016 01:58, Nordlöw wrote:
> https://github.com/nordlow/units-d

From there:

>  * Example:
>  * ---
>  * alias BaseUnit!("Ampere", "A") Ampere;
>  * enum ampere = Ampere.init;
>  * // or
>  * enum ampere = baseUnit!("Ampere", "A");
>  * ---

I dislike that the type depends only on the given name. This effectively means that the names are in a global namespace.

For example:

----
import experimental.units;

struct A
{
    alias BaseUnit!("Ampere", "A") Ampere;
    enum ampere = Ampere.init;
}

struct B
{
    alias BaseUnit!("Ampere", "A") Ampere;
    enum ampere = Ampere.init;
}
----

A.Ampere and B.Ampere are the same type here. I think it would be more useful if they weren't. When two unrelated sources define a new unit with the same name, then they shouldn't be compatible. Think of all the different kinds of pounds and miles there used to be in the world.

I can't think of a truly nice way to accomplish this, though. As far as I see, it needs a mixin of some kind.

Like this, for example:

----
enum string baseUnit = q{
    {
        static struct BaseUnit {/* ... */}
        return BaseUnit.init;
    }()
};

struct A
{
    enum ampere = mixin(baseUnit);
}

struct B
{
    enum ampere = mixin(baseUnit);
}

static assert(!is(typeof(A.ampere) == typeof(B.ampere)));
----
April 01, 2016
On Thursday, 31 March 2016 at 23:58:54 UTC, Nordlöw wrote:
> I've put David Nadlinger work together with my tweaks on top at
>
> https://github.com/nordlow/units-d
>
> to make it easier to experiment with.
>
> PR are very welcome.

Nice work.

I have yet to play around with it, but this is definitely going to need pre-made symbols for the United States customary system like SI has before it goes into Phobos.
April 01, 2016
On Friday, 1 April 2016 at 19:03:03 UTC, ag0aep6g wrote:
> I dislike that the type depends only on the given name. This effectively means that the names are in a global namespace.
[snip]
> I can't think of a truly nice way to accomplish this, though. As far as I see, it needs a mixin of some kind.

The usual way to fix it would be to include __FILE__ and __LINE__ in the template arguments:

// In units/package.d:
struct BaseUnit(string name, string symbol = null, string file = __FILE__, size_t line = __LINE__)
{
   // ...
}

// in foo.d:
import experimental.units;
struct A
{
     // Actual type is BaseUnit!("Ampere", "A", "foo", 5):
    alias BaseUnit!("Ampere", "A") Ampere;
    enum ampere = Ampere.init;
}

struct B
{
     // Actual type is BaseUnit!("Ampere", "A", "foo", 12):
    alias BaseUnit!("Ampere", "A") Ampere;
    enum ampere = Ampere.init;
}

void main() {
    assert(!is(B.Ampere == A.Ampere));
}

--
  Simen
April 01, 2016
On 01.04.2016 22:59, Simen Kjaeraas wrote:
> The usual way to fix it would be to include __FILE__ and __LINE__ in the
> template arguments:

Right, no mixin this way. I wouldn't call this "truly nice", though.

It depends on code formatting to work. Put everything on one line and it breaks. Significant whitespace is a pain when generating code. Though this is not nearly as bad as significant indentation, of course.

__FILE__ also kind of breaks separate compilation. All object files have to be compiled from the same directory. Otherwise __FILE__ will be different.

__LINE__ has a similar (maybe even more obscure) issue. Add or remove a newline before compiling dependent modules and things break. Usually, one recompiles all dependents when a dependency changes, but a significant newline, really?
April 01, 2016
On Friday, 1 April 2016 at 21:46:35 UTC, ag0aep6g wrote:
> On 01.04.2016 22:59, Simen Kjaeraas wrote:
>> The usual way to fix it would be to include __FILE__ and __LINE__ in the
>> template arguments:
>
> Right, no mixin this way. I wouldn't call this "truly nice", though.
>
> It depends on code formatting to work. Put everything on one line and it breaks. Significant whitespace is a pain when generating code. Though this is not nearly as bad as significant indentation, of course.
>
> __FILE__ also kind of breaks separate compilation. All object files have to be compiled from the same directory. Otherwise __FILE__ will be different.
>
> __LINE__ has a similar (maybe even more obscure) issue. Add or remove a newline before compiling dependent modules and things break. Usually, one recompiles all dependents when a dependency changes, but a significant newline, really?

I kinda agree. And looking at https://dlang.org/spec/traits.html, I see there's __MODULE__, which would probably be a better choice than __FILE__.

As for __LINE__, what we'd want is basically something like __CONTEXT__, which doesn't exist, but might be the .mangleof of the surrounding scope:

struct S(string ctx = __CONTEXT__) {
    pragma(msg, ctx);
}

S!() a; // "3foo"

void bar() {
    S!() b; // "_D3foo3barFZv"
}

struct S2 {
    S!() c; // "S3foo2S2"
    void baz() {
        S!() d; // "_D3foo2S23bazMFZv"
    }
}

That'd remove the problem of significant whitespace. In fact, it'd also eliminate the need for __MODULE__ in this case.

Still though, that's not enough if we want this to work:

void foo() {
    alias a = Foo!(); alias b = Foo!();
    assert(!isSame!(a, b));
}

We could also add __COLUMN__, which would be the horizontal index of the instantiation's beginning:

    foo(3, Bar!3.baz);
//         ^Here. Position 11.

Next problem:

void main() {
    pragma(msg, __LINE__);
    mixin("pragma(msg, __LINE__);\npragma(msg, __LINE__);");
    pragma(msg, __LINE__);
}

That prints '4' twice - once for the actual line 4, the other for the second line of the mixin. However, __FILE__ is different, so I guess __CONTEXT__ could also be.

--
  Simen
April 02, 2016
On Friday, 1 April 2016 at 22:54:53 UTC, Simen Kjaeraas wrote:
> On Friday, 1 April 2016 at 21:46:35 UTC, ag0aep6g wrote:
>> On 01.04.2016 22:59, Simen Kjaeraas wrote:
>>> The usual way to fix it would be to include __FILE__ and __LINE__ in the
>>> template arguments:
>>
>> Right, no mixin this way. I wouldn't call this "truly nice", though.
>>
>> It depends on code formatting to work. Put everything on one line and it breaks. Significant whitespace is a pain when generating code. Though this is not nearly as bad as significant indentation, of course.
>>
>> __FILE__ also kind of breaks separate compilation. All object files have to be compiled from the same directory. Otherwise __FILE__ will be different.
>>
>> __LINE__ has a similar (maybe even more obscure) issue. Add or remove a newline before compiling dependent modules and things break. Usually, one recompiles all dependents when a dependency changes, but a significant newline, really?
>
> I kinda agree. And looking at https://dlang.org/spec/traits.html, I see there's __MODULE__, which would probably be a better choice than __FILE__.
>
> As for __LINE__, what we'd want is basically something like __CONTEXT__, which doesn't exist, but might be the .mangleof of the surrounding scope:
>
> struct S(string ctx = __CONTEXT__) {
>     pragma(msg, ctx);
> }
>
> S!() a; // "3foo"
>
> void bar() {
>     S!() b; // "_D3foo3barFZv"
> }
>
> struct S2 {
>     S!() c; // "S3foo2S2"
>     void baz() {
>         S!() d; // "_D3foo2S23bazMFZv"
>     }
> }
>
> That'd remove the problem of significant whitespace. In fact, it'd also eliminate the need for __MODULE__ in this case.
>
> Still though, that's not enough if we want this to work:
>
> void foo() {
>     alias a = Foo!(); alias b = Foo!();
>     assert(!isSame!(a, b));
> }
>
> We could also add __COLUMN__, which would be the horizontal index of the instantiation's beginning:
>
>     foo(3, Bar!3.baz);
> //         ^Here. Position 11.
>
> Next problem:
>
> void main() {
>     pragma(msg, __LINE__);
>     mixin("pragma(msg, __LINE__);\npragma(msg, __LINE__);");
>     pragma(msg, __LINE__);
> }
>
> That prints '4' twice - once for the actual line 4, the other for the second line of the mixin. However, __FILE__ is different, so I guess __CONTEXT__ could also be.
>
> --
>   Simen

What is needed is Lisp's gensym construct.
April 02, 2016
On Friday, 1 April 2016 at 22:54:53 UTC, Simen Kjaeraas wrote:

...
> I kinda agree. And looking at https://dlang.org/spec/traits.html, I see there's __MODULE__, which would probably be a better choice than __FILE__.
>
 I think adding something like __UNIQUE_NAME__ to predefined constants will allow to avoid all these perversions. The only thing to think here about uniqueness scope.
April 04, 2016
On Saturday, 2 April 2016 at 01:19:45 UTC, Meta wrote:
> What is needed is Lisp's gensym construct.

That's basically what I said, no? :p

One problem of lisp's gensym (if we were to use it in D) is that it's simply a monotonically increasing number with a global prefix. It's perfect for the language it's in, and all but useless in D.

For this very reason, I have made a stab at implementing __GENSYM__ in D. It follows basically the ideas I outlined above, and spits out a string on the form MANGLE_#, with # being a counter for the given mangled name:

module bar;
struct Ham(string gensym = __GENSYM__) {
    pragma(msg, gensym);
}
struct Eggs(string gensym = __GENSYM__) {
    pragma(msg, gensym);
}

// ===========================

module foo;
import bar;

pragma(msg, __GENSYM__); // 3foo_1

void main() {
    pragma(msg, __GENSYM__); // _Dmain_1
    Ham!() a; // _Dmain_3bar_1
    Ham!() b; // _Dmain_3bar_2
    assert(!is(typeof(a) == typeof(b)));
    Eggs!() c; // _Dmain_3bar_3
    S2!() d; // _Dmain_3foo_1
}

struct Qux {
    pragma(msg, __GENSYM__); // 3foo3Qux_1
    void baz() {
        pragma(msg, __GENSYM__); // _D3foo3Qux3bazMFZv_1
        Ham!() a; // _D3foo3Qux3bazMFZv_3bar_1
    }
}

struct S2(string gensym = __GENSYM__) {
    pragma(msg, gensym);
}

Should I file an enhancement for this?

--
  Simen