Clay language

Andrei Alexandrescu SeeWebsiteForEmail at erdani.org
Thu Dec 30 08:00:20 PST 2010


On 12/30/10 9:00 AM, Steven Schveighoffer wrote:
> On Wed, 29 Dec 2010 16:14:11 -0500, Andrei Alexandrescu
> <SeeWebsiteForEmail at erdani.org> wrote:
>
>> On 12/29/10 2:58 PM, Steven Schveighoffer wrote:
>>> On Wed, 29 Dec 2010 15:38:27 -0500, Andrei Alexandrescu
>>> <SeeWebsiteForEmail at erdani.org> wrote:
>>>
>>>> On 12/29/10 2:10 PM, Steven Schveighoffer wrote:
>>>>> On Wed, 29 Dec 2010 14:42:53 -0500, Andrei Alexandrescu
>>>>> <SeeWebsiteForEmail at erdani.org> wrote:
>>>>>
>>>>>> On 12/27/10 6:55 PM, Andrei Alexandrescu wrote:
>>>>>>> On 12/27/10 12:35 PM, bearophile wrote:
>>>>>>>> Through Reddit I have found a link to some information about the
>>>>>>>> Clay
>>>>>>>> language, it wants to be (or it will be) a C++-class language, but
>>>>>>>> it's not tied to C syntax. It shares several semantic similarities
>>>>>>>> with D too. It looks like a cute language:
>>>>>>>> https://github.com/jckarter/clay/wiki/
>>>>>>> [snip]
>>>>>>>
>>>>>>> FWIW I just posted a response to a question asking for a comparison
>>>>>>> between Clay and D2.
>>>>>>>
>>>>>>> http://www.reddit.com/r/programming/comments/es2jx/clay_programming_language_wiki/
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>
>>>>>> That thread is shaping up more and more interesting because it's
>>>>>> turning into a discussion of generic programming at large.
>>>>>
>>>>> I wanted to address your post in the reddit discussion regarding the
>>>>> issue of operator overloads not being virtual:
>>>>>
>>>>> "This non-issue has been discussed in the D newsgroup. You can
>>>>> implement
>>>>> virtuals on top of non-virtuals efficiently, but not vice versa."
>>>>>
>>>>> I've found some very real problems with that, when implementing
>>>>> operator
>>>>> overloads in dcollections. It's forced me to use the (yet to be
>>>>> deprecated) opXXX forms. Specifically, you cannot use covariance with
>>>>> templated functions without repeating the entire implementation in the
>>>>> derived class.
>>>>
>>>> Glad you're bringing that up. Could you please post an example that
>>>> summarizes the issue?
>>>
>>> With D1:
>>>
>>> interface List
>>> {
>>> List opCat(List other);
>>> }
>>>
>>> class LinkList : List
>>> {
>>> LinkList opCat(List other) {...}
>>> }
>>>
>>> With D2:
>>>
>>> interface List
>>> {
>>> List doCat(List other); // implement this in derived class
>>> List opBinary(string op)(List other) if (op == "~")
>>> { return doCat(other); }
>>> }
>>>
>>> class LinkList : List
>>> {
>>> LinkList doCat(List other) {...}
>>> }
>>>
>>> // usage;
>>>
>>> LinkList ll = new LinkList(1, 2, 3);
>>> ll = ll ~ ll; // works with D1, fails on D2, "can't assign List to
>>> LinkList"
>>>
>>> Solution is to restate opBinary in all dervied classes with *exact same
>>> code* but different return type. I find this solution unacceptable.
>>
>> I understand, thanks for taking the time to share. The solution to
>> this matter as I see it is integrated with another topic - usually you
>> want to define groups of operators, which means you'd want to define
>> an entire translation layer from static operators to overridable ones.
>>
>> Here's the code I suggest along those lines. I used a named function
>> instead of a template to avoid 4174:
>>
>> template translateOperators()
>> {
>> auto ohPeeCat(List other) { return doCat(other); }
>> }
>>
>> interface List
>> {
>> List doCat(List other); // implement this in derived class
>> }
>>
>> class LinkList : List
>> {
>> LinkList doCat(List other) { return this; }
>> mixin translateOperators!();
>> }
>>
>> void main(string[] args)
>> {
>> LinkList ll = new LinkList;
>> ll = ll.ohPeeCat(ll);
>> }
>>
>> The translateOperators template would generally define a battery of
>> operators depending on e.g. whether appropriate implementations are
>> found in the host class (in this case LinkList).
>
> I'm assuming you meant this (once the bug is fixed):
>
> template translateOperators()
> {
> auto opBinary(string op)(List other) {return doCat(other);} if (op == "~")
> }
>
> and adding this mixin to the interface?

In fact if the type doesn't define doCat the operator shouldn't be 
generated.

   auto opBinary(string op, T)(T other) {return doCat(other);}
     if (op == "~" && is(typeof(doCat(other))))

The other thing that I didn't mention and that I think it would save you 
some grief is that this is meant to be a once-for-all library solution, 
not code that needs to be written by the user. In fact I'm thinking the 
mixin should translate from the new scheme to the old one. So for people 
who want to use operator overloading with inheritance we can say: just 
import std.typecons and mixin(translateOperators()) in your class 
definition. I think this is entirely reasonable.

> I find this solution extremely convoluted, not to mention bloated, and
> how do the docs work? It's like we're going back to C macros! This
> operator overloading scheme is way more trouble than the original.

How do you mean bloated? For documentation you specify in the 
documentation of the type what operators it supports, or for each named 
method you specify that operator xxx forwards to it.

> The thing I find ironic is that with the original operator overloading
> scheme, the issue was that for types that define multiple operator
> overloads in a similar fashion, forcing you to repeat boilerplate code.
> The solution to it was a mixin similar to what you are suggesting.
> Except now, even mundane and common operator overloads require verbose
> template definitions (possibly with mixins), and it's the uncommon case
> that benefits.

Not at all. The common case is shorter and simpler. I wrote the chapter 
on operator overloading twice, once for the old scheme and once for the 
new one. It uses commonly-encountered designs for its code samples. The 
chapter and its code samples got considerably shorter in the second 
version. You can't blow your one example into an epic disaster.

> So really, we haven't made any progress (mixins are still
> required, except now they will be more common). I think this is one area
> where D has gotten decidedly worse. I mean, just look at the difference
> above between defining the opcat operator in D1 and your mixin solution!

I very strongly believe the new operator overloading is a vast 
improvement over the existing one and over most of today's languages. We 
shouldn't discount all of its advantages and focus exclusively on 
covariance, which is a rather obscure facility.

Using operator overloading in conjunction with class inheritance is 
rare. Rare as it is, we need to allow it and make it convenient. I 
believe this is eminently possible along the lines discussed in this thread.

> As a compromise, can we work on a way to forward covariance, or to have
> the compiler reevaluate the template in more derived types?

I understand. I've had this lure a few times, too. The concern there is 
that this is a potentially surprising change.


Andrei


More information about the Digitalmars-d mailing list