Jump to page: 1 2
Thread overview
Request for a more powerful template specialization feature
Jul 15
Enamex
5 days ago
jmh530
5 days ago
jmh530
July 14
Dear all,

Template specializations are a great feature in D. They allow the programmer to create template specializations but they can also be a powerful way of constraining templates by implementing only the specializations that you need. In contrast template constraints can quickly become very complex for the programmer to write and reason about.

Template specializations should be extended to allow multiple lists of types to be implemented together. For example this ...

template Construct(R: Union{double, int}, W: Union{string, char, dchar})
{
    auto Construct(R, W)(R r, W w)
    {
        ....
    }
}

The same definition would be allowed for all 6 combinations of {double, int} and {string, char, dchar} (and no more! Unless specified otherwise and/or more generally). This would remove the need to manually write these for all combinations or resort to constraints.

In addition for some use cases it is a nicer way of doing unions than using the current union keyword. Union{double, int} could be a compile-time construct indicating that the type can be either an actual double or int. It can replace the use of union in cases where you want a substitution of either double or int for this Union rather than a union type. In addition, variants return variants rather than "properly" typed data.

It would be good to get comments and suggestions before I write a DIP.

Thank you in advance.

July 14
On 7/14/17 2:19 PM, data pulverizer wrote:
> template Construct(R: Union{double, int}, W: Union{string, char, dchar})

template Construct(R, W)
if ((is(R == double) || is(R == int))
    && (is(W == string) || is(W == char) || is(W == dchar))

> It would be good to get comments and suggestions before I write a DIP.

An effective way of improving the state of affairs would be to create a PR that makes the constraint easier to read and write, e.g.:

among!(R, double, int) && among!(W, string, char, dchar)

In fact it's surprising it hasn't been proposed yet.

Andrei
July 14
On Friday, July 14, 2017 3:49:05 PM MDT Andrei Alexandrescu via Digitalmars- d wrote:
> On 7/14/17 2:19 PM, data pulverizer wrote:
> > template Construct(R: Union{double, int}, W: Union{string, char, dchar})
>
> template Construct(R, W)
> if ((is(R == double) || is(R == int))
>      && (is(W == string) || is(W == char) || is(W == dchar))

Yeah, I tend to forget that template specialization even exists in D, and I _really_ hate the fact that : in template specializations doesn't mean what it means in is expressions (equality instead of implicit conversion). So, I wish that we didn't even have template specializations, but presumably, it's not worth the breakage to remove them. The one thing that they do have going for them over template constraints though is that if you overload with template constraints, you tend to have to duplicate the constraint with the reverse condition, which you don't have to do with template specializations. But smart use of static ifs inside of templated functions can mitigate that problem, and ultimately, I think that template specialization is a redundant feature.

> > It would be good to get comments and suggestions before I write a DIP.
>
> An effective way of improving the state of affairs would be to create a PR that makes the constraint easier to read and write, e.g.:
>
> among!(R, double, int) && among!(W, string, char, dchar)
>
> In fact it's surprising it hasn't been proposed yet.

On seeing this thread, that's the first thing I thought of as well, and I assumed that it already existed but didn't take the time to track it down. Off the top of my head, I'd guess that you'd use staticIndexOf an check for -1 to implement it, which should be pretty straight forward, albeit slightly more verbose. Thinking on it now, I think that that's what I've done in the past.

- Jonathan M Davis

July 14
On 07/14/2017 04:29 PM, Jonathan M Davis via Digitalmars-d wrote:
> I'd guess that you'd use staticIndexOf an check for
> -1 to implement it

Given that constraints are user-facing, having a forwarding one-liner may be justifiable. -- Andrei
July 14
On Friday, July 14, 2017 4:45:10 PM MDT Andrei Alexandrescu via Digitalmars- d wrote:
> On 07/14/2017 04:29 PM, Jonathan M Davis via Digitalmars-d wrote:
> > I'd guess that you'd use staticIndexOf an check for
> > -1 to implement it
>
> Given that constraints are user-facing, having a forwarding one-liner may be justifiable. -- Andrei

Possibly. It's hard to know sometimes when a one line wrapper is a big enough boost to usability to standardize. I could see this being such a case.

staticIndexOf is a bit unwieldy, and I've rarely used it, but when I have, I think that I've always used it to check whether somehing is present rather than getting an actual index.

Another tough example would be takeWhile/dropWhile. We already of until and find, which do the same things respectively but with the opposite predicate such that they're arguably too simple to be worth adding, but at the same time, if you have to negate your predicates often enough, having takeWhile and dropWhile would be a definite boon (also, for many folks, takeWhile and dropWhile are obvious in a way that until or find might not be - especially until). Timon was complaining bitterly the other day about the lack of takeWhile:

https://issues.dlang.org/show_bug.cgi?id=4535

IIRC, I tried to add those several years ago, but you vetoed them on the grounds that simply negating the predicate was trvial, which is certainly true, albeit less user-friendly. I don't know what the correct answer is though. Sometimes, it's clear that adding a one-liner is a pointless wrapper, whereas in other cases, it seems like a definite usabiliy improvement.

- Jonathan M Davis

July 14
On Friday, 14 July 2017 at 18:19:03 UTC, data pulverizer wrote:
> Dear all,
>
> Template specializations are a great feature in D. They allow the programmer to create template specializations but they can also be a powerful way of constraining templates by implementing only the specializations that you need. In contrast template constraints can quickly become very complex for the programmer to write and reason about.
>
> Template specializations should be extended to allow multiple lists of types to be implemented together. For example this ...
>
> template Construct(R: Union{double, int}, W: Union{string, char, dchar})
> {
>     auto Construct(R, W)(R r, W w)
>     {
>         ....
>     }
> }
>
> The same definition would be allowed for all 6 combinations of {double, int} and {string, char, dchar} (and no more! Unless specified otherwise and/or more generally). This would remove the need to manually write these for all combinations or resort to constraints.
>
> In addition for some use cases it is a nicer way of doing unions than using the current union keyword. Union{double, int} could be a compile-time construct indicating that the type can be either an actual double or int. It can replace the use of union in cases where you want a substitution of either double or int for this Union rather than a union type. In addition, variants return variants rather than "properly" typed data.
>
> It would be good to get comments and suggestions before I write a DIP.
>
> Thank you in advance.

I am aware that this suggestion touches the language and the compiler - and may significant implications. I would like to know whether this could be done without too much effort and whether it would break anything else?

If you are writing lots of overloaded templates, constraints can have unintended behaviour because you end up telling the compiler what not to do rather than what to do. The above Unions are clear and simple, easy to use and should result in cleaner more robust code.

July 14
On Friday, 14 July 2017 at 22:25:15 UTC, data pulverizer wrote:
> I am aware that this suggestion touches the language and the compiler - and may significant implications. I would like to know whether this could be done without too much effort and whether it would break anything else?
>
> If you are writing lots of overloaded templates, constraints can have unintended behaviour because you end up telling the compiler what not to do rather than what to do. The above Unions are clear and simple, easy to use and should result in cleaner more robust code.

In addition with template specializations you get constraints for free. If I implement template overloads which are all specializations, the compiler gives me a very informative error if I step outside the pre-defined set of implementations. That's just brilliant - exactly what you want! I immediately know when I see that error what the issue is. Am I being naive? Why are constraints better?
July 14
On Friday, 14 July 2017 at 22:49:18 UTC, data pulverizer wrote:
> On Friday, 14 July 2017 at 22:25:15 UTC, data pulverizer wrote:
>> I am aware that this suggestion touches the language and the compiler - and may significant implications. I would like to know whether this could be done without too much effort and whether it would break anything else?
>>
>> If you are writing lots of overloaded templates, constraints can have unintended behaviour because you end up telling the compiler what not to do rather than what to do. The above Unions are clear and simple, easy to use and should result in cleaner more robust code.
>
> In addition with template specializations you get constraints for free. If I implement template overloads which are all specializations, the compiler gives me a very informative error if I step outside the pre-defined set of implementations. That's just brilliant - exactly what you want! I immediately know when I see that error what the issue is. Am I being naive? Why are constraints better?

One important characteristic about constraints, is that they are not bound to types.
Also they can from conjunctions and disjunctions.
Combined with ctfe they are very flexible and powerful.

I do not know how you would do the same with specializations.
Then again being unfamiliar with template-specializations I might be overlooking something.
July 14
On Friday, 14 July 2017 at 23:04:48 UTC, Stefan Koch wrote:
> One important characteristic about constraints, is that they are not bound to types.
> Also they can from conjunctions and disjunctions.
> Combined with ctfe they are very flexible and powerful.
>
> I do not know how you would do the same with specializations.
> Then again being unfamiliar with template-specializations I might be overlooking something.

True ... it doesn't have to be specializations or constraints, as you have said constraints are important for conjunctions and disjunctions. I should not have said "why are constraints better than specializations". However when you are dealing with a know finite set of know types or constructs - which I do frequently, it is useful to be able to be able to specify the exact behaviour for individuals and different groups clearly and succinctly. For instance when writing mathematical algorithms that should apply differently to different constructs, its much easier to say "do this for that specific construct" and "do something else for some group" and so on without having to repeat negating cases.

A practical case might be dealing with two library "kinds" each having around 8 possible type specifications where some associated methods are related. Sometimes methods will be the same for combinations of types both within each kind and across both kind combinations. Specializations that allow multiple values are really good for these cases.

I currently  use string mixins to code-gen my template specializations - its a good-enough but visually inelegant solution.

July 15
On Friday, 14 July 2017 at 19:49:05 UTC, Andrei Alexandrescu wrote:
> On 7/14/17 2:19 PM, data pulverizer wrote:
>> template Construct(R: Union{double, int}, W: Union{string, char, dchar})
>
> template Construct(R, W)
> if ((is(R == double) || is(R == int))
>     && (is(W == string) || is(W == char) || is(W == dchar))
>
>> It would be good to get comments and suggestions before I write a DIP.
>
> An effective way of improving the state of affairs would be to create a PR that makes the constraint easier to read and write, e.g.:
>
> among!(R, double, int) && among!(W, string, char, dchar)
>
> In fact it's surprising it hasn't been proposed yet.
>
> Andrei

But specializations are quite different from constraints, no? Constraints wouldn't help when the template name is overloaded and passes the constraint checks of several different template implementations; specializations narrow things down for overload resolution.

Unless I'm misunderstanding the OP or everyone else in the thread somehow :/
« First   ‹ Prev
1 2