November 19, 2009
== Quote from Andrei Alexandrescu (SeeWebsiteForEmail@erdani.org)'s article
> > 2.  After thinking about this some more, the big issue I see is ref opIndex.  We
> > can either:
> >     a.  Disallow it for both UniqueArray and ArrayBuilder.
> >     b.  Allow it for both UniqueArray and ArrayBuilder and accept
> >         that a sufficiently dumb programmer can invalidate the
> >         guarantees of UniqueArray by taking the address of one of the
> >         elements and saving it somewhere.  Probably a bad idea, since
> >         assumeUnique() already works for the careful programmer, and
> >         UniqueArray is supposed to provide ironclad guarantees.
> >     c.  Don't define opIndex in the abstract base class at all, thus
> >         making Array almost useless as an abstract base class.
> Welcome to my demons :o).
> One possibility that I thought of for a long time would be to disallow
> taking the address of a ref. That reduces the scope of the problem but
> doesn't eliminate it:
> void main() {
>      auto a = new UniqueArray(10);
>      fun(a[0], a);
> }
> void fun(ref int a, UniqueArray b)
> {
>     auto imm = b.toImmutable();
>     // here a is a mutable alias into an immutable array!!!
> }
> So that doesn't work, but I thought I'd mention it :o).
> Another possibility is to expose opIndex to return by value and also
> opIndexAssign that sets the value. That would be a no-no in C++ because
> copying is arbitrarily expensive, but I have a feeling that in D it is
> sensible to consider and foster that all objects should be defined to be
> cheap to copy (e.g. refcounting, COW etc.) If we go by the notion that
> in D we can always assume copy costs are reasonable, this last
> possibility would work. With the newfangled operators, it would even
> work beautifully because you can do all sorts of things like a[1] += 4
> without ever exposing a ref to the user.
> Andrei

Eureka!  This is one of those corner cases where non-virtual, non-final class member functions are tremendously useful.  Fortunately, we can simulate them my making opIndex a template.

abstract class ArrayBase(T) {
    T* ptr;

    T opIndex(I : size_t)(I index) {
        return ptr[index];
    }
}

class ArrayBuilder(T) : ArrayBase!(T) {
    ref T opIndex(I : size_t)(I index) {
        return ptr[index];
    }
}

void main() {
    auto ab = new ArrayBuilder!(float);
    auto num = ab[0];  // Yes, this segfaults, but we only care
                       // that it compiles.
}


If the compile time type is an ArrayBase or any subtype of ArrayBase that doesn't override opIndex, then we get value return.  If it's an ArrayBuilder, we get ref return.  This happens regardless of what the runtime type is.  Precisely what we wanted.
November 19, 2009
On Wed, Nov 18, 2009 at 8:33 PM, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> wrote:

> I am thinking that representing operators by their exact token representation is a principled approach because it allows for unambiguous mapping,

There used to be an argument floating around that using 'opAdd" was better than "op+" because the former encourages people to only overload the method to do things that resemble addition.  Whereas op+ says I'm just a symbol, do whatever you want with me.

I never really bought into it... but it was what I was told years ago
when I complained that opSub opMul etc were harder to remember than
need be. :-)
I guess D will have to change it's story now.

--bb
November 19, 2009
Walter Bright wrote:
> Andrei Alexandrescu wrote:
>> If we use @safe and @trusted to indicate unequivocally "no escape", then there is no analysis to be done - the hard part of the analysis has already been done manually by the user.
> 
> The problem then becomes:
> 
> T[] foo(T[] t) { return t; }
> 
> T[] bar()
> {
>    T[3] a;
>    return foo(a);
> }

The "no escape" rule only applies to pointers, not arrays. Translating your example to pointers:

T* foo(T* t) { return t; }

T* bar() {
    T[] a;
    return foo(a.ptr);
}

Steve Schweighoffer has pointed out a while ago that the compiler cannot assume a scope of a returned value to be any larger than the scopes of its parameters. Your example and the example above are canonical and the most complicated analysis I know of in SafeD. It is admittedly a huge complication, but just something that you'll need to pay attention to when implementing.


Andrei
November 19, 2009
Justin Johansson wrote:
> Justin Johansson wrote:
>> aarti_pl wrote:
>>> Andrei Alexandrescu pisze:
>>>> aarti_pl wrote:
>>>>> aarti_pl pisze:
>>>>>> Andrei Alexandrescu pisze:
>>>>>>> 2. User-defined operators must be revamped. Fortunately Don already put in an important piece of functionality (opDollar). What we're looking at is a two-pronged attack motivated by Don's proposal:
>>>>>>>
>>>>>>> http://prowiki.org/wiki4d/wiki.cgi?LanguageDevel/DIPs/DIP7
>>>>>>>
>>>>>>> The two prongs are:
>>>>>>>
>>>>>>> * Encode operators by compile-time strings. For example, instead of the plethora of opAdd, opMul, ..., we'd have this:
>>>>>>>
>>>>>>> T opBinary(string op)(T rhs) { ... }
>>>>>>>
>>>>>>> The string is "+", "*", etc. We need to design what happens with read-modify-write operators like "+=" (should they be dispatch to a different function? etc.) and also what happens with index-and-modify operators like "[]=", "[]+=" etc. Should we go with proxies? Absorb them in opBinary? Define another dedicated method? etc.
>>>>>>>
>>>>>>> * Loop fusion that generalizes array-wise operations. This idea of Walter is, I think, very good because it generalizes and democratizes "magic". The idea is that, if you do
>>>>>>>
>>>>>>> a = b + c;
>>>>>>>
>>>>>>> and b + c does not make sense but b and c are ranges for which a.front = b.front + c.front does make sense, to automatically add the iteration paraphernalia.
>>>>>>>
>>>>> (..)
>>>>>>> Andrei
>>>>>>
>>>>>> I kinda like this proposal. But I would rather call template like below:
>>>>>>
>>>>>> T opInfix(string op)(T rhs) { ... }
>>>>>> T opPrefix(string op)(T rhs) { ... }
>>>>>> T opPostfix(string op)(T rhs) { ... }
>>>>>>
>>>>>> and allow user to define her own operators (though it doesn't have to be done now).
>>>>>>
>>>>>> I know that quite a few people here doesn't like to allow users to define their own operators, because it might obfuscate code. But it doesn't have to be like this. Someone here already mentioned here that it is not real problem for programs in C++. Good libraries don't abuse this functionality.
>>>>>>
>>>>>> User defined operators would allow easy definition of Domain Specific Languages in D. I was already writing about it some time ago:
>>>>>>
>>>>>> http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=81026 
>>>>>>
>>>>>> http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=81352 
>>>>>>
>>>>>>
>>>>>> BR
>>>>>> Marcin Kuszczak
>>>>>> (aarti_pl)
>>>>>
>>>>> Of course for opPrefix/opPostfix signatures will be different:
>>>>> T opPrefix(string op)() { ... }
>>>>> T opPostfix(string op)() { ... }
>>>>>
>>>>> Sorry for mistake.
>>>>>
>>>>> BR
>>>>> Marcin Kuszczak
>>>>> (aarti_pl)
>>>>
>>>> I think we'll solve postfix "++" without requiring the user to define it. Do you envision user-defined postfix operators?
>>>>
>>>> Andrei
>>>
>>> Well, maybe something like below:
>>>
>>> auto a = 2²;  //(quadratic power of 2)
>>> auto a = 5!;  //factorial of 5
>>> auto a = 2Ƴ + 3ɛ; //solving equations
>>> auto weight = 5kg; //units of measurement
>>>
>>> The point is that this covers whole scope of operators. In fact even built-in operators could be defined using it.
>>>
>>> Postfix operator ++ can be defined using prefix operator++ just by delegation and this can be default.
>>>
>>> Best Regards
>>> Marcin Kuszczak
>>> (aarti_pl)
>>
>> Marcin demonstrates a valid point.
>>
>> If there is going to be this feature creep, the feature should be complete with all the usual variants of operator arity and notation (i.e. prefix/postfix/infix).  Otherwise it really it's only two-thirds baked.
>>
>> -- Justin Johansson
> 
> I meant to say "Iff there is ..." as in "if and only if".
> 
> Like others, I'm not completely sold on the idea at all.  Also it's probably not possible to squeeze something as long as this on to a short list.
> 
> All or nothing please.

I disagree with this false choice.

http://en.wikipedia.org/wiki/False_dilemma#False_choice


Andrei
November 20, 2009
Adam D. Ruppe wrote:
> What if there was some magic defined at the top of the file or something, that
> could be pulled out without parsing the whole thing?
> 
> pragma(DEFINE_BINARY_OPERATOR, "^^");
> 
> Then, later in that module, and any that import it, you'd treat the ^^ as
> a user defined operator token.

Still mixing up lexing, parsing, and semantic analysis.

I know it's tempting, but it only leads to madness.
November 20, 2009
On Thu, Nov 19, 2009 at 04:00:25PM -0800, Walter Bright wrote:
> I know it's tempting, but it only leads to madness.

Besides, I think this is something that a lot of people might ask for, but then once they got it, they wouldn't actually use it.

Imagine getting a library that uses a really cool character as an operator... just to find that you can't type that character on your keyboard anyway. Or maybe you can, but it is a big pain, so it isn't worth the effort.

Or getting that cool character in source that you edit in a primitive editor or font that can't display it, so you just see gibberish.

In theory, those aren't problems. But in practice, I think that would make
the feature very rarely used. There just aren't that many easy to
type / ubiquitous to display symbols that have meaning not already
defined in the language.


-- 
Adam D. Ruppe
http://arsdnet.net
November 20, 2009
Bill Baxter wrote:
> On Wed, Nov 18, 2009 at 8:33 PM, Andrei Alexandrescu
> <SeeWebsiteForEmail@erdani.org> wrote:
> 
>> I am thinking that representing operators by their exact token
>> representation is a principled approach because it allows for unambiguous
>> mapping,
> 
> There used to be an argument floating around that using 'opAdd" was
> better than "op+" because the former encourages people to only
> overload the method to do things that resemble addition.  Whereas op+
> says I'm just a symbol, do whatever you want with me.
> 
> I never really bought into it... but it was what I was told years ago
> when I complained that opSub opMul etc were harder to remember than
> need be. :-)
> I guess D will have to change it's story now.
> 
> --bb

FWIW, I'm not buying into that either. May be one of those cases in which community's experience and insight has slowly obviated the party line.

Andrei
November 20, 2009
Bill Baxter, el 19 de noviembre a las 14:16 me escribiste:
> > What I found hackish about it is that the code is a string manipulation mess. You can already do a string manipulation mess to programatically implement all the operator overloading.
> 
> This is true, but if you leave it entirely up to the programmer and
> string mixins, the mess is much more messy.
> You're going to end up with code like this:
> 
> mixin(genBinaryOp("+", q{MyType}, q{MyType rhs}, q{
>     return this.impl + rhs.impl;
> }));
> 
> 
> instead of this:
> MyType opBinary(string op : "+")(MyType rhs)  {
>     return this.impl + rhs.impl;
> }
> 
> I would have a hard time defending the former as the recommended D style. But the latter is not so bad.  It looks like a regular template declaration, and code is code, not a string.

But in this case you gained nothing comparing to opAdd(), where opBinary()
is useful is where you use string mixins to avoid implementing the
operators one by one.

I know the opBinary() approach is less ugly than writing the code youself, but a library solution could be provided in the meantime (in the unexistent std.mixin module; which BTW, can't be named that way because "mixin" is a keyword ;).

-- 
Leandro Lucarella (AKA luca)                     http://llucax.com.ar/
----------------------------------------------------------------------
GPG Key: 5F5A8D05 (F8CD F9A7 BF00 5431 4145  104C 949E BFB6 5F5A 8D05)
----------------------------------------------------------------------
When I was a child
I caught a fleeting glimpse
Out of the corner of my eye.
I turned to look but it was gone
I cannot put my finger on it now
The child is grown,
The dream is gone.
I have become comfortably numb.
November 20, 2009
On Thu, Nov 19, 2009 at 5:17 PM, Leandro Lucarella <llucax@gmail.com> wrote:
> Bill Baxter, el 19 de noviembre a las 14:16 me escribiste:
>> > What I found hackish about it is that the code is a string manipulation mess. You can already do a string manipulation mess to programatically implement all the operator overloading.
>>
>> This is true, but if you leave it entirely up to the programmer and
>> string mixins, the mess is much more messy.
>> You're going to end up with code like this:
>>
>> mixin(genBinaryOp("+", q{MyType}, q{MyType rhs}, q{
>>     return this.impl + rhs.impl;
>> }));
>>
>>
>> instead of this:
>> MyType opBinary(string op : "+")(MyType rhs)  {
>>     return this.impl + rhs.impl;
>> }
>>
>> I would have a hard time defending the former as the recommended D style. But the latter is not so bad.  It looks like a regular template declaration, and code is code, not a string.
>
> But in this case you gained nothing comparing to opAdd(), where opBinary()
> is useful is where you use string mixins to avoid implementing the
> operators one by one.
>
> I know the opBinary() approach is less ugly than writing the code youself, but a library solution could be provided in the meantime (in the unexistent std.mixin module; which BTW, can't be named that way because "mixin" is a keyword ;).

My point was that the best you're going to be able to do with a library solution (even for multiple operators) would be to provide a "genBinaryOp" function, making your code look something like this:

mixin(genBinaryOps("+ - * /", q{MyType}, q{MyType rhs}, q{
    return this.impl $op rhs.impl;
}));

Or I guess with more effort you could probably have something like this:

mixin(genBinaryOps("+ - * /", q{
   MyType $opName(MyType rhs) {
       return this.impl $op rhs.impl;
   }
}));

But I still find the plain template idea cleaner for the user than a string generation function that does heck knows what.

--bb
November 20, 2009
On Thu, Nov 19, 2009 at 1:35 PM, KennyTM~ <kennytm@gmail.com> wrote:

> Speaking of which, how to map opDiv_r into its opBinary!()() equivalent?

I would guess there will simply be an opBinary_r!("/") in addition to
opBinary!("/").

--bb