January 25, 2013
On 01/24/2013 05:56 PM, Jonathan M Davis wrote:
> On Thursday, January 24, 2013 17:13:36 Ali Çehreli wrote:

>> Normally, const vs. immutable parameters are different in the way that
>> they accept arguments:
>>
>> - immutable is "limiting" because it insists that the argument is immutable.
>>
>> - const is "welcoming" because it accepts mutable, const, and immutable.

> I've never heard anyone describe it that way before.

We must find easier ways of explaining these semantics. I appreciate the examples that you have provided but we can't expect newcomers to extract meaning out of this.

We must be able to come up with a few function design guidelines. My choice of words like "welcoming" and "limiting" are attempts at finding sanity.

> const is more generic,
> whereas immutable is more specific. But from the compiler's point of view,
> passing a non-const object to a const function means doing a conversion (from
> mutable to const)

That is news to me. There shouldn't be any conversion from non-const to const because that is a conversion that should have no runtime effect. As Era Scarecrow put it, it means "this variable cannot be changed by me". As I understand it, the compiler should merely enforce that guarantee at compile time without performing any conversions.

>, whereas calling a function of the same type does not
> require a conversion. I believe that _that_ is the core of why ref takes
> precedence over const.

If const really requires a conversion then we have a language design problem here.

> So, if you have

[...]

These examples are great but as I said, they don't help the programmer at function signature stage.

immutable is easy: If I am going to need immutable data, then I make the parameter immutable.

By-value is also understandable: I don't care about the original. Additionally, when a 'ref' overload exists, by-value gives me the chance to move because only the rvalue can be bound to by-value. Pretty indirect way of thinking but we can deal with it...

const should be easy as well: "I am not going to modify". Just like in C++... Unfortunately that is not the case.

Ali

January 25, 2013
On Friday, 25 January 2013 at 04:26:18 UTC, Jonathan M Davis wrote:
> With templated code, it can be important. But then again, if there's no point in having a non-const overload, you can simply not declare any overloads without const. You only run into problems when you mix const and non-const.
>
> The compiler has to be able to deal with various combinations of const and ref regardless of what it actually makes sense to declare. The only way that I can think of to get rid of that problem is to make it illegal to declare both const and non-const overloads at the same time, which seems unnecessarily restrictive (especially with regards to generic code), even if it doesn't normally make sense to overload on const.

 True, but still it seems overtly annoying. I noticed most of this from TDPL. pg 257

[quote]
  The problem is that opAssign as defined expects a ref Widget, that is, an lvalue of type Widget. To accept assignment from rvalue in addition to lvalues, Widget must define two assignment operators:

  import std.algorithm;

  struct Widget {
    private int[] array;
    ref Widget opAssign(ref Widget rhs) {
      array = rhs.array.dup;
      return this;
    }
    ref Widget opAssign(Widget rhs) {
      swap(array, rhs.array);
      return this;
    }
  }

 There's no more .dup in the version takint an rvalue. Why? Well the rvalue (with it's array in tow) is practically owned by the second opAssign: It was copied prior to entering the function and will be destroyed just before the function returns. This means there's no more need to duplicate rhs.array because nobody will miss it. Swapping rhs.array with this.array is enough. When opAssign returns, rhs goes away with this's old array, and this stays with rhs's old array--perfect conservation of state.

 We now could remove the first overload of opAssign altogether: The one taking rhs by value takes care of everything (lvalues are automatically converted to rvalues). But keeping the lvalue version allows for a useful optimization: instead of .duping the source, opAssign can check whether the current array has space for accommodating the new contents, in which case an overwrite is enough.
[/quote]

 The whole time I look at the example code, I can see how the ref Widget can be const and continue to work perfectly, but the non-ref cannot be const (without ugly unsafe casting). But if you try to pass const data to opAssign it will either try to copy it (if it's POD or postblit it), or fail outright. In my mind I shouldn't have to double the functions to have it 'do the right thing', even if it's just to forward them. This is not a case where 'auto ref' could help, as lvalue/rvalue distinction needs to be kept.
January 25, 2013
On Thursday, January 24, 2013 21:38:42 Ali Çehreli wrote:
> If const really requires a conversion then we have a language design problem here.

No. const T is _not_ the same type as T, therefore assigning a T to a const T _is_ a type conversion. _Any_ time that a variable of one type is assigned to a variable of another type is a conversion regardless of the types involved. True, the bits of the object won't have changed when converting from T to const T (assuming that no alias this has been declared for the conversion anyway), but it's still a type conversion.

>  > So, if you have
> 
> [...]
> 
> These examples are great but as I said, they don't help the programmer at function signature stage.
> 
> immutable is easy: If I am going to need immutable data, then I make the parameter immutable.
> 
> By-value is also understandable: I don't care about the original. Additionally, when a 'ref' overload exists, by-value gives me the chance to move because only the rvalue can be bound to by-value. Pretty indirect way of thinking but we can deal with it...
> 
> const should be easy as well: "I am not going to modify". Just like in C++... Unfortunately that is not the case.

I don't see what could be done to make it simpler other than making it illegal to overload on constness or refness, which would cause other problems. When you're dealing with a variety of overloads, it's always going to get complicated on some level. Stuff like auto ref and inout help, but it's complicated by its very nature. Besides, the simple rule of making sure that either all of your overloads match in terms of constness or that you have overloads for both const and non-const parameters of matching refness solves the problem. And if you screw it up, it's obvious _very_ quickly, since it causes infinite recursion.

- Jonathan M Davis
January 27, 2013
> no match
> match with im­plicit con­ver­sions
> match with con­ver­sion to const
> exact match

Explain me, why this code prints:

Error: A() is not an lvalue

[code]
import std.stdio;

struct A { }

void foo(A a, float r) {
	
}

void foo(const A a, float r) {

}

void foo(ref A a, float r) {
	
}

void main()
{
   foo(A(), 12);
}
[/code]

Why is the ref A a functions chosen?
I create already a pull request but a explanation would be nice.
January 27, 2013
pull request -> bug report. I'm a bit confused today.
1 2
Next ›   Last »