November 19, 2009
grauzone, el 19 de noviembre a las 03:47 me escribiste:
> Does the current proposal make things simpler at all? All you're doing is to enable the programmer to "fix" the clumsy semantics by throwing lots of CTFE onto the problem. Why not generate the operator functions with CTFE in the first place...

I was about to say that, the solution is a hack. I could understand a hack if there were no other way to do it, but you can generate the code for the opXxx using CTFE/string mixins already: we already have a hackish solution. I don't think adding a new hack would be nice (specially when it will be a big change).

Maybe a not-so-hackish solution can be found when AST macros get implemented.

-- 
Leandro Lucarella (AKA luca)                     http://llucax.com.ar/
----------------------------------------------------------------------
GPG Key: 5F5A8D05 (F8CD F9A7 BF00 5431 4145  104C 949E BFB6 5F5A 8D05)
----------------------------------------------------------------------
Borrowing money from a friend is like having sex. It just completely changes
the relationship.
	-- George Constanza
November 19, 2009
dsimcha wrote:
> == Quote from Andrei Alexandrescu (SeeWebsiteForEmail@erdani.org)'s article
>> dsimcha wrote:
>>> == Quote from Andrei Alexandrescu (SeeWebsiteForEmail@erdani.org)'s article
>>>> dsimcha wrote:
>>>>> == Quote from Andrei Alexandrescu (SeeWebsiteForEmail@erdani.org)'s article
>>>>>> 3. It was mentioned in this group that if getopt() does not work in
>>>>>> SafeD, then SafeD may as well pack and go home. I agree. We need to make
>>>>>> it work. Three ideas discussed with Walter:
>>>>>> * Allow taking addresses of locals, but in that case switch allocation
>>>>>> from stack to heap, just like with delegates. If we only do that in
>>>>>> SafeD, behavior will be different than with regular D. In any case, it's
>>>>>> an inefficient proposition, particularly for getopt() which actually
>>>>>> does not need to escape the addresses - just fills them up.
>>>>> IMHO this is a terrible solution.  SafeD should not cause major ripple
> effects for
>>>>> pieces of code that don't want to use it.  I'm all for safe defaults even if
>>>>> they're less efficient or less flexible, but if D starts sacrificing performance
>>>>> or flexibility for safety **even when the programmer explicitly asks it not
> to**,
>>>>> then it will officially have become a bondage and discipline language.
>>>>>
>>>>> Furthermore, as you point out, having the semantics of something vary in subtle
>>>>> ways between SafeD and unsafe D is probably a recipe for confusion.
>>>>>
>>>>>
>>>>>> * Allow @trusted (and maybe even @safe) functions to receive addresses
>>>>>> of locals. Statically check that they never escape an address of a
>>>>>> parameter. I think this is very interesting because it enlarges the
>>>>>> common ground of D and SafeD.
>>>>> This is a great idea if it can be implemented.  Isn't escape analysis a pretty
>>>>> hard thing to get right, though, especially when you might not have the source
>>>>> code to the function being called?
>>>> Escape analysis is difficult when you don't have information about the
>>>> functions you're passing the pointer to. For example:
>>>> void fun(int* p) {
>>>>      if (condition) gun(p);
>>>> }
>>>> Now the problem is that fun's escape-or-not behavior depends on flow
>>>> (i.e. condition) and on gun's escaping behavior.
>>>> 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.
>>> But then the @safe or @trusted function wouldn't be able to escape pointers to
>>> heap or static data segment memory either, if I understand this proposal
> correctly.
>> Yah. The question is to what extent is that necessary.
>> Andrei
> 
> Too kludgey for me.  I'd rather just see ref parameters get fixed and just don't
> allow taking the address of locals in @safe functions.  I'd say that, except in
> low-level systems programming that would probably not be @safe for other reasons
> anyhow, there would be very few good if any good reasons to take the address of a
> local if reference tuples just worked.

Unfortunately it's more complicated than that. getopt takes pairs of strings and pointers. The strings don't necessarily have to be lvalues, so constraining getopt to only take references is not the right solution.

Andrei
November 19, 2009
Leandro Lucarella wrote:
> grauzone, el 19 de noviembre a las 03:47 me escribiste:
>> Does the current proposal make things simpler at all? All you're
>> doing is to enable the programmer to "fix" the clumsy semantics by
>> throwing lots of CTFE onto the problem. Why not generate the
>> operator functions with CTFE in the first place...
> 
> I was about to say that, the solution is a hack. I could understand a hack
> if there were no other way to do it, but you can generate the code for the
> opXxx using CTFE/string mixins already: we already have a hackish
> solution. I don't think adding a new hack would be nice (specially when it
> will be a big change).
> 
> Maybe a not-so-hackish solution can be found when AST macros get
> implemented.

I am thinking that representing operators by their exact token representation is a principled approach because it allows for unambiguous mapping, testing with if and static if, and also allows saving source code by using only one string mixin. It would take more than just a statement that it's hackish to convince me it's hackish. I currently don't see the hackishness of the approach, and I consider it a vast improvement over the current state of affairs.

I'd be grateful if you argued your point further and hopefully suggested an approach that is better. I want us to move fast with this. So it's just the right time to contribute.


Andrei
November 19, 2009
On Nov 19, 09 07:48, bearophile wrote:
> Andrei Alexandrescu:
>
>> * 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.
>
> Can you show an example of defining an operator, like a minus, with that?
>
> In my set data structure I'd like to define "<=" among two sets as "is subset". Can that design allow me to overload just<= and>= ? (opCmp is not enough here).
>
> Bye,
> bearophile

float opCmp(X other) {
  if (this cannot compare with other)
    return float.nan;
  else
    ...
}
November 19, 2009
grauzone wrote:
> Andrei Alexandrescu wrote:
>> grauzone wrote:
>>> Andrei Alexandrescu wrote:
>>>> The rewrite is done long after lexing, so no low-level problems there.
>>>
>>> Oh, I thought it would let you introduce new operators. But it's only about the existing ones.
>>>
>>> I find the idea to identify the operator using a string very sloppy and sillyl just like using string mixins for small delegates in std.algorithm etc.; but you'd probably say "it works and is useful" and "it's short" and "it solves the current problem", so... whatever.
>>
>> We're trying to improve on the current situation, which forces the user to manually define a lot of small functions. If you have convincing reasons to argue that the current state of affairs is actually better, I'm all ears - both Walter and I could use less work, particularly if the outcome sucks (see e.g. T[new]). Also, if you have ideas on how things could be done in a way that you'd find not sloppy and not silly, that would be even better.
> 
> If I had a better proposal, I'd post it. I'm just saying that's it's a bad hack, that _although_ solves the problem, will have negative side effects for other reasons.

What are those other reasons? I'd be grateful if you could spell a complete argument.

> Does the current proposal make things simpler at all? All you're doing is to enable the programmer to "fix" the clumsy semantics by throwing lots of CTFE onto the problem. Why not generate the operator functions with CTFE in the first place...

I'm afraid there is a confusion, in which case let me dispel it. There's no CTFE involved. The way people would use the feature would be in one of the following ways:

1. Just use "if" or "static if" the old-school:

T opBinary(string op)(T rhs) {
    static if (op == "+") return data + rhs.data;
    else static if (op == "-") return data - rhs.data;
    ...
    else static assert(0, "Operator "~op~" not implemented");
}

This has not a lot of advantage beyond the fact that you don't need to remember the mappings from symbols to their names.

2. Use one mixin expression:

T opBinary(string op)(T rhs) {
    return mixin("data "~op~" rhs.data");
}

3. Combine the two in various ways, also use restricted templates, forward to virtual functions, etc. etc.

The improvements are the following:

1. There is no need to memorize or look up a long table of symbol-name correspondence (e.g. + is opAdd, etc.)

2. There are source code savings because most often types want to overload operators en masse, not just a couple of them. In that case there is a code explosion of many functions. Worse, if the code needs to do something clever (e.g. Variant or expression templates), the cleverness must be spread all over, or effort needs to be spent into mapping back names to their symbols, as Variant actually does. It's not a pretty sight, so I'd rather have the language facilitate good usage instead of it facilitating bad usage that must be reverted to good usage.

3. The syntax is extensible, for example Walter wanted for the longest time to make !<>= etc. overridable but couldn't bring himself to write abominable names like opNotGreaterThanOrWhatever. Now it's very simple to integrate all symbols properly and without aggravation.

The cons I see are:

1. The user who writes even the old-school operators must have a basic understanding of compile-time parameterized functions.

2. Existing code gets broken.

3. The template functions can't be virtual (fixable with a forwarding thunk written once and for all).

Any more thoughts, please let them known. Again, this is the ideal time to contribute. But "meh, it's a hack" is difficult to discuss.


Andrei
November 19, 2009
grauzone wrote:
> Andrei Alexandrescu wrote:
>> 3. It was mentioned in this group that if getopt() does not work in SafeD, then SafeD may as well pack and go home. I agree. We need to make it work. Three ideas discussed with Walter:
> 
> If that's such an issue, why don't you just change it and use a struct defined by the user? structs are natural name-value pairs that work at compile time, and they can always be returned from functions.
> 
>> 4. Allow private members inside a template using the eponymous trick:
>>
>> template wyda(int x) {
>>    private enum geeba = x / 2;
>>    alias geeba wyda;
>> }
> 
> Why is this important?
> 
> Apart from this...
> 
> Whatever happened to the concurrency stuff? Do you really want users to deal with the broken and incomplete implementation right now? What about the issues of constructing immutable data? (Eh, are we supposed to cast from mutable data, and hope everything goes right?) Right now, multithreaded programming in D2 is probably even more a pain than in C++.
> 
> Also, you should fix the auto-flattening of tuples before it's too late. I think everyone agrees that auto-flattening is a bad idea, and that tuples should be nestable. Flattening can be done manually with an unary operator.
> 
> (Introducing sane tuples (e.g. unify type and value tuples, sane and short syntax, and all that) can wait for later if it must. Introducing these can be downwards compatible, I hope.)

Non-flattening should be on the list but I am very afraid the solution would take a long time to design, implement, and debug. I must discuss this with Walter.

Andrei
November 19, 2009
Travis Boucher wrote:
> grauzone wrote:
>>
>> If I had a better proposal, I'd post it. I'm just saying that's it's a bad hack, that _although_ solves the problem, will have negative side effects for other reasons.
>>
>> Does the current proposal make things simpler at all? All you're doing is to enable the programmer to "fix" the clumsy semantics by throwing lots of CTFE onto the problem. Why not generate the operator functions with CTFE in the first place...
> 
>  From my point of view (trying different ways of implementing something as simple as a vector), this makes things much simpler without sacrificing functionality.
> 
> Personally, I'd love to see an unknownMethod(string method)(...) thing implemented as well, but that might be asking for too much (and might sacrifice performance in some cases).

Oh, that needs to be on the list! Thanks for reminding me, I added it to my list.

Andrei
November 19, 2009
On Wed, 18 Nov 2009 23:33:54 -0500, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> wrote:

> Leandro Lucarella wrote:
>> grauzone, el 19 de noviembre a las 03:47 me escribiste:
>>> Does the current proposal make things simpler at all? All you're
>>> doing is to enable the programmer to "fix" the clumsy semantics by
>>> throwing lots of CTFE onto the problem. Why not generate the
>>> operator functions with CTFE in the first place...
>>  I was about to say that, the solution is a hack. I could understand a hack
>> if there were no other way to do it, but you can generate the code for the
>> opXxx using CTFE/string mixins already: we already have a hackish
>> solution. I don't think adding a new hack would be nice (specially when it
>> will be a big change).
>>  Maybe a not-so-hackish solution can be found when AST macros get
>> implemented.
>
> I am thinking that representing operators by their exact token representation is a principled approach because it allows for unambiguous mapping, testing with if and static if, and also allows saving source code by using only one string mixin. It would take more than just a statement that it's hackish to convince me it's hackish. I currently don't see the hackishness of the approach, and I consider it a vast improvement over the current state of affairs.
>
> I'd be grateful if you argued your point further and hopefully suggested an approach that is better. I want us to move fast with this. So it's just the right time to contribute.
>
>
> Andrei

For what it's worth, I used aliases of a template binary op function in order to do all the operator overloads of a small vec class. So I like your solution.
November 19, 2009
> 6. There must be many things I forgot to mention, or that cause grief to many of us. Please add to/comment on this list.
> 

Sorry I'm not familiar with any prior discussion on this or even where it's at (basically I'm in D1 world), but what about

constructors for structs

Justin Johansson
November 19, 2009
Andrei Alexandrescu wrote:
> I am thinking that representing operators by their exact token representation is a principled approach because it allows for unambiguous mapping, testing with if and static if, and also allows saving source code by using only one string mixin. It would take more than just a statement that it's hackish to convince me it's hackish. I currently don't see the hackishness of the approach, and I consider it a vast improvement over the current state of affairs.

Isn't opBinary just a reduced-functionality version of opUnknownMethod (or whatever that is/was going to be called)?

T opBinary(string op)(T rhs) {
    static if (op == "+") return data + rhs.data;
    else static if (op == "-") return data - rhs.data;
    ...
    else static assert(0, "Operator "~op~" not implemented");
}

T opUnknownMethod(string op)(T rhs) {
    static if (op == "opAdd") return data + rhs.data;
    else static if (op == "opSub") return data - rhs.data;
    ...
    else static assert(0, "Method "~op~" not implemented");
}

I'd much rather have opUnknownMethod than opBinary.  If if I have opUnknownMethod, then opBinary becomes redundant.


-- 
Rainer Deyke - rainerd@eldwood.com