www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - user defined literals

reply Trass3r <un known.com> writes:
Just came across C++0x user defined literals (via user defined suffixes):
http://en.wikipedia.org/wiki/C%2B%2B0x#User-defined_literals

They are implemented via something like:

std::complex<double> operator "" i(double d) // cooked form, e.g. 15i
{ 
    return std::complex(0, d);
}

Another C++0x typical <nasty syntax> solution for a nice feature.
It should be possible to incorporate this into D quite easily via something
along the lines of:

Complex!T opSuffix(U data) // whatever the name should be

where U can be a number type or a string (see the link for cooked vs. raw form)

Just like traditional operator overloading in D this would only mean a tiny
layer of syntactic sugar on top of standard language features. Given D's CTFE
capabilities you could probably even define compile-time literals without
further modifications (C++0x needs variadic templates and constexpr for that!
:D).

My actual question is: are there any non-apparent pitfalls or side effects that
might complicate or speak against an implementation?
Mar 25 2011
next sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Fri, 25 Mar 2011 09:32:12 -0400, Trass3r <un known.com> wrote:

 Just came across C++0x user defined literals (via user defined  
 suffixes): http://en.wikipedia.org/wiki/C%2B%2B0x#User-defined_literals

 They are implemented via something like:

 std::complex<double> operator "" i(double d) // cooked form, e.g. 15i
 {
     return std::complex(0, d);
 }

 Another C++0x typical <nasty syntax> solution for a nice feature.
 It should be possible to incorporate this into D quite easily via  
 something along the lines of:

 Complex!T opSuffix(U data) // whatever the name should be

 where U can be a number type or a string (see the link for cooked vs.  
 raw form)

 Just like traditional operator overloading in D this would only mean a  
 tiny layer of syntactic sugar on top of standard language features.  
 Given D's CTFE capabilities you could probably even define compile-time  
 literals without further modifications (C++0x needs variadic templates  
 and constexpr for that! :D).

 My actual question is: are there any non-apparent pitfalls or side  
 effects that might complicate or speak against an implementation?

What is the need for this? Just to replace complex(0, 5) with 5i? I don't see the huge benefit. Also, complex is the clear and obvious beneficiary because it's so widely understood. What are other use cases? BigXXX for sure, but Rational probably cannot be covered by this since you need two numbers. I simply find the complication of the language is not worth the benefits. -Steve
Mar 25 2011
next sibling parent reply Trass3r <un known.com> writes:
Steven Schveighoffer Wrote:
 What is the need for this?  Just to replace complex(0, 5) with 5i?  I  
 don't see the huge benefit.

Furthermore I didn't mean to praise its usefulness. I was actually asking about practical issues that could arise if it _was_ implemented the way I presented.
 I simply find the complication of the language is not worth the benefits.

 Also, complex is the clear and obvious beneficiary

But keeping complex literals in dmd as syntactic sugar beyond the grave of complex types is another story.
 What are other use cases?  BigXXX for sure, but Rational  
 probably cannot be covered by this since you need two numbers.

auto dist = 1km; sin(90deg);
Mar 25 2011
next sibling parent Trass3r <un known.com> writes:
Lars T. Kyllingstad Wrote:
   enum I = Complex!float(0, 1);
   auto z = 5.0 + 3*I;
 
 The compiler should be able to fold the sum into a single constant at 
 compile time.

It is indeed capable of doing that :) But it only does it if you force it to (e.g. via enum z). With auto it will perform 2 opBinaryRight calls at runtime.
Mar 25 2011
prev sibling next sibling parent Trass3r <un known.com> writes:
Steven Schveighoffer Wrote:
 Aside from the parsing issues, which I think would be minor, it's more of  
 a problem with anyone and his mother is allowed to define literal types.

Yeah that's true.
 That is, someone could have 1fud and I'm not sure what the hell that  
 means, so I have to go find out where that xyz is defined.  It must be  
 global, since literals can be used anywhere, so it can be anywhere.

Well the user defined literals could only be used in places where the module containing the proper opSuffix implementation is imported. The search for that doesn't really differ from the search for any other poorly named function or type or whatever. I do agree though that suffixes would mostly be very short and could get very cryptic.
 auto dist = 1km;
 sin(90deg);

These are good points, but of course, units are open to interpretation. Not every unit has a standard abbreviation, and not every abbreviation is unique. These things are highly context sensitive. For example, I could say: auto mytime = 1h + 3m + 4s; auto mydistance = 15m;

Probably one of the biggest problems of the whole concept.
 Not only that, but it's compile-time, meaning there is no actual call to  
 some operator processor to generate the timestamp.

You forget CTFE ;)
Mar 25 2011
prev sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 3/25/2011 8:14 AM, Trass3r wrote:
 Units, I think.
 auto dist = 1km;
 sin(90deg);

One difficulty with the C++0x approach is that the operator"" needed to implement it is a global function. Global functions work fine until you wind up with two km functions with different purposes, and then things just don't work anymore. In D, you can have user defined literals of the form: km!1 km!"1" km(1) implemented using CTFE. Adding yet another syntax for it seems to be of marginal utility. C++0x needed to add the new syntax because its CTFE ability is minimal, and C++ template metaprogramming cannot process strings.
Mar 25 2011
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 3/25/11 10:28 AM, Walter Bright wrote:
 On 3/25/2011 8:14 AM, Trass3r wrote:
 Units, I think.
 auto dist = 1km;
 sin(90deg);

One difficulty with the C++0x approach is that the operator"" needed to implement it is a global function. Global functions work fine until you wind up with two km functions with different purposes, and then things just don't work anymore. In D, you can have user defined literals of the form: km!1 km!"1" km(1) implemented using CTFE. Adding yet another syntax for it seems to be of marginal utility. C++0x needed to add the new syntax because its CTFE ability is minimal, and C++ template metaprogramming cannot process strings.

All good points. I should also add that the user-defined literal syntax is considered an albatross in certain C++ circles. Andrei
Mar 25 2011
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Fri, 25 Mar 2011 11:14:26 -0400, Trass3r <un known.com> wrote:

 Steven Schveighoffer Wrote:
 What is the need for this?  Just to replace complex(0, 5) with 5i?  I
 don't see the huge benefit.

sugar. Furthermore I didn't mean to praise its usefulness. I was actually asking about practical issues that could arise if it _was_ implemented the way I presented.

OK, I understand.
 I simply find the complication of the language is not worth the  
 benefits.

the language.

Aside from the parsing issues, which I think would be minor, it's more of a problem with anyone and his mother is allowed to define literal types. That is, someone could have 1fud and I'm not sure what the hell that means, so I have to go find out where that xyz is defined. It must be global, since literals can be used anywhere, so it can be anywhere. I just find that I have very little need for custom literals with all the wealth of features that D has, so I would be hesitant to stray from the f(value) style which is easily understood and readily parsable.
 Also, complex is the clear and obvious beneficiary

the compiler specifically so that 5 + 3i is transformed to Complex(5,3) rather than 5 + Complex(0, 3)

We have quite powerful mixin abilities such that I think something like: complex!("5 + 3i") could be rewritten as Complex!double(5, 3) without compiler help. And like you said, the feature as proposed does not do the ideal thing.
 What are other use cases?  BigXXX for sure, but Rational
 probably cannot be covered by this since you need two numbers.

auto dist = 1km; sin(90deg);

These are good points, but of course, units are open to interpretation. Not every unit has a standard abbreviation, and not every abbreviation is unique. These things are highly context sensitive. For example, I could say: auto mytime = 1h + 3m + 4s; auto mydistance = 15m; Notice that 3 minutes and 15 meters can't really be distinguished by the compiler. I like the idea of having this be more context-sensitive and also allow for the feature without language changes: auto mytime = tstamp!("1h + 3m + 4s"); Not only that, but it's compile-time, meaning there is no actual call to some operator processor to generate the timestamp. I believe we already have this for octal. If we could define a more standard way to create these things, then I think it would be a good step. -Steve
Mar 25 2011
prev sibling next sibling parent "Lars T. Kyllingstad" <public kyllingen.NOSPAMnet> writes:
On Fri, 25 Mar 2011 11:46:08 -0400, Steven Schveighoffer wrote:
 On Fri, 25 Mar 2011 11:14:26 -0400, Trass3r <un known.com> wrote:
 Steven Schveighoffer Wrote:
 Also, complex is the clear and obvious beneficiary

the compiler specifically so that 5 + 3i is transformed to Complex(5,3) rather than 5 + Complex(0, 3)

We have quite powerful mixin abilities such that I think something like: complex!("5 + 3i") could be rewritten as Complex!double(5, 3) without compiler help. And like you said, the feature as proposed does not do the ideal thing.

I believe the plan is to keep complex literals even after the built-in complex types are gone, but I don't think this is necessary at all. The following works just fine: enum I = Complex!float(0, 1); auto z = 5.0 + 3*I; The compiler should be able to fold the sum into a single constant at compile time. -Lars
Mar 25 2011
prev sibling next sibling parent "Lars T. Kyllingstad" <public kyllingen.NOSPAMnet> writes:
On Fri, 25 Mar 2011 12:36:11 -0400, Trass3r wrote:

 Lars T. Kyllingstad Wrote:
   enum I = Complex!float(0, 1);
   auto z = 5.0 + 3*I;
 
 The compiler should be able to fold the sum into a single constant at
 compile time.

It is indeed capable of doing that :) But it only does it if you force it to (e.g. via enum z). With auto it will perform 2 opBinaryRight calls at runtime.

Even when you compile with -O? -Lars
Mar 25 2011
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Fri, 25 Mar 2011 12:54:46 -0400, Trass3r <un known.com> wrote:

 Steven Schveighoffer Wrote:

 Not only that, but it's compile-time, meaning there is no actual call to
 some operator processor to generate the timestamp.

You forget CTFE ;)

CTFE is only used when you are defining a compile-time constant, or creating a static initializer. literals are not CTFE'd. For example, an array literal still calls a runtime function to construct the array. The call could also be optimized out, but when the literal is a generic template (not necessarily a function), you can *force* it to be compile-time, regardless of optimizations or inlining. -Steve
Mar 25 2011
prev sibling parent Trass3r <un known.com> writes:
 It is indeed capable of doing that :) But it only does it if you force
 it to (e.g. via enum z). With auto it will perform 2 opBinaryRight calls
 at runtime.

Even when you compile with -O?

Yep. It doesn't even seem to optimize away an unused variable: void main() { auto z = 5.0 + 3*I; } Or I've done something wrong.
Mar 25 2011
prev sibling parent Trass3r <un known.com> writes:
 Complex!T opSuffix(U data) // whatever the name should be

Of course the suffix itself is missing: Complex!T opSuffix!(string suffix="i")(U data)
Mar 25 2011