September 07, 2012
On 2012-09-07 13:34, Kevin McTaggart wrote:
> I've been looking at migrating a reasonably large ship motion library
> (tens of thousands of lines) from C# to D.  I've become quite
> enthusiastic about D, and most of my problems have been relatively minor
> (e.g., inconsistent bugs with std.container.Array, would like orange
> serialization to give me an error telling me I didn't register a class
> before calling serialize).

Please report any bugs about Orange to https://github.com/jacob-carlborg/orange and I'll see what I can do about it.

-- 
/Jacob Carlborg
September 08, 2012
Given:

void func (ref int[], int)

If ref/out were required at the call site, this destroys UFCS.

int[] array;
array.func(0); // error, ref not specified by caller

Or would one expect something like:

(ref array).func(0);

...put simply, I hope not.

This suggestion has come up a couple times before, and each time failed to gain traction.  I wouldn't mind it as an option -- possibly even as a recommendation in most library code -- but as a requirement it honestly just gives me a headache.  Generally speaking, if a parameter being ref/out is surprising, there is something wrong with the design.  (There are times it is non-obvious in otherwise good code, this seems uncommon.)

For example, calling one of the *InPlace functions from Phobos, I immediately expect 'ref' -- otherwise, how to modify "in place" in a reliable (ie, non-allocating) manner?  Likewise, 'out' -- used pretty rarely in the first place -- sits in the same place pointers typically do, as auxiliary returns.  The category of functions/methods which use them is pretty self consistent.  (What few corner cases remain would be better served by cleaning up tuples to make them more sane as return values.)

Given:

bool checkedEmitJ (Op, Params, ref const(Scope), Address, out Address)

I really don't want to have to call it as:

auto handled = checkedEmitJ(Op.CJmp, parms, ref _scope, suggest, out lval);

When the 'ref' and 'out' parameters listed are specified by the design and consistent across all the "-emit-" functions.
September 08, 2012
On 08/09/2012 14:05, Chris Nicholson-Sauls wrote:
> Given:
>
> void func (ref int[], int)
>
> If ref/out were required at the call site, this destroys UFCS.
>
> int[] array;
> array.func(0); // error, ref not specified by caller

For UFCS, ref should be implied. Also for 'const ref' parameters, callsite ref should not be necessary.

> This suggestion has come up a couple times before, and each time failed
> to gain traction.

But given that C, C#, (Go?) and Rust have the same philosophy that argument modification should be explicit for value types, it is arguably important. It might be nice to have for D3.

> I wouldn't mind it as an option -- possibly even as a
> recommendation in most library code -- but as a requirement it honestly
> just gives me a headache.

As an option it would serve no practical purpose other than documentation.

> Generally speaking, if a parameter being
> ref/out is surprising, there is something wrong with the design.  (There
> are times it is non-obvious in otherwise good code, this seems uncommon.)

Yes but unfortunately bad design is going to be a reality sometimes. Also there is a difference between 'surprising' and 'obvious'. Callsite ref makes it quicker to understand code for a negligible cost, occasionally a lot quicker if you're coming back to code you haven't seen for a while.

> For example, calling one of the *InPlace functions from Phobos, I
> immediately expect 'ref' -- otherwise, how to modify "in place" in a
> reliable (ie, non-allocating) manner?  Likewise, 'out' -- used pretty
> rarely in the first place -- sits in the same place pointers typically
> do, as auxiliary returns.  The category of functions/methods which use
> them is pretty self consistent. (What few corner cases remain would be
> better served by cleaning up tuples to make them more sane as return
> values.)
>
> Given:
>
> bool checkedEmitJ (Op, Params, ref const(Scope), Address, out Address)
>
> I really don't want to have to call it as:
>
> auto handled = checkedEmitJ(Op.CJmp, parms, ref _scope, suggest, out lval);
>
> When the 'ref' and 'out' parameters listed are specified by the design
> and consistent across all the "-emit-" functions.

The point is that the signature of checkedEmitJ is not always there when you are reading code that calls checkedEmitJ, so the ref and out parameters are useful in saving the reader time. Making code easier to read is much more important than making it easier to write.

When I first saw:
swap(ref a, ref b);

I thought that looked ugly with the refs, but now I don't mind it. IMO it's only functions whose arguments are all passed by ref that don't really need annotation, and these are infrequent. All others stand to benefit.


September 08, 2012
Nick Treleaven:

> IMO it's only functions whose arguments are all passed by ref that don't really need annotation, and these are infrequent. All others stand to benefit.

For arguments that use "const ref", "in ref", "immutable ref" I think ref at the call site is not so important.

Bye,
bearophile
September 08, 2012
On Friday, 7 September 2012 at 11:33:41 UTC, Kevin McTaggart wrote:
> I've been looking at migrating a reasonably large ship motion library (tens of thousands of lines) from C# to D.  I've become quite enthusiastic about D, and most of my problems have been relatively minor (e.g., inconsistent bugs with std.container.Array, would like orange serialization to give me an error telling me I didn't register a class before calling serialize).  I suggest that the language require ref and out when calling functions, as C# requires.  This would make code easier to understand, and would also eliminate the problem I had when the wrong function from the following choices was mistakenly called:
>
> parseLineInts(string text, int positionStart, out int j0, out int j1)
>
> parseLineInts(string text, out int j0, out int j1, out int j2)
>
> I note that the second function calls another function as follows:
> int positionStart = 1;
> parseLineInts(text, positionStart, j0, j1, j2);
>
> I look forward to seeing feedback from D experts.  This is the only significant change that I could think of recommending for the language.

+1. The fact that a value type is modified should be information the programmer can see at a glance.




Andrei Alexandrescu wrote:
> Actually the darndest thing is that C# has retired the syntax in 5.0 (it used to be required up until 4.0). Apparently users complained it was too unsightly.

Citation? I'm using C# 5.0 with Visual Studios 2012 on Windows 8 right now and ref/out are still required at the call sight of functions.

September 08, 2012
On Saturday, September 08, 2012 17:05:25 Nick Treleaven wrote:
> As an option it would serve no practical purpose other than documentation.

It's worse than that. It's not even documentation that you can trust. If it were optional, and ref were used in a function call, then you know that the argument is being passed by ref (since presumably, you'll get a compilation error if it's not), but if there is no ref, it tells you _nothing_. It could be ref, but it might not be. And if you used ref at the call site frequently, then the lack of ref would end up giving the false impression that the argument wasn't being passed by ref in the case where the ref was forgotten.

There's some argument for requiring it (though at this point in its development, I think that it's too late to make it required even if it were determined that that were desirable), but making it optional is _definitely_ a bad idea IMHO.

- Jonathan M Davis
September 08, 2012
Agh, sent a private mail again. I don't seem to be the only one though.
Has there been a thunderbird UI change?

On 09/08/2012 06:05 PM, Nick Treleaven wrote:
> On 08/09/2012 14:05, Chris Nicholson-Sauls wrote:
>> Given:
>>
>> void func (ref int[], int)
>>
>> If ref/out were required at the call site, this destroys UFCS.
>>
>> int[] array;
>> array.func(0); // error, ref not specified by caller
>
> For UFCS, ref should be implied.

Why? UFCS means uniform function call syntax.

> Also for 'const ref' parameters, callsite ref should not be necessary.
>

The callee might escape a pointer to the argument. Which is
'non-obvious' as well when there is no callsite ref.

>> This suggestion has come up a couple times before, and each time failed
>> to gain traction.
>
> But given that C, C#, (Go?) and Rust have the same philosophy that
> argument modification should be explicit

It is already explicit. The information is just not repeated at the
call site.

> for value types, it is arguably important.

This is not necessarily a valid conclusion. Popularity does not imply
importance.

> It might be nice to have for D3.
>
>> I wouldn't mind it as an option -- possibly even as a
>> recommendation in most library code -- but as a requirement it honestly
>> just gives me a headache.
>
> As an option it would serve no practical purpose other than documentation.
>

Requiring it makes lack of call site annotations carry some information.
The only practical purpose would still be documentation.


>> Generally speaking, if a parameter being
>> ref/out is surprising, there is something wrong with the design.  (There
>> are times it is non-obvious in otherwise good code, this seems uncommon.)
>
> Yes but unfortunately bad design is going to be a reality sometimes.

Bad design never should be a motivation for adding language features.
In fact, it seems to be a motivation for creating languages that have
few features.

> Also there is a difference between 'surprising' and 'obvious'. Callsite
> ref makes it quicker to understand code for a negligible cost,
> occasionally a lot quicker if you're coming back to code you haven't
> seen for a while.
>

IMHO it is better left to the future D editor. It can highlight ref/out
arguments in a distinct style. Why clutter the language core even more
with somewhat complex rules specifying in what cases call site ref is
required?

> ...
> The point is that the signature of checkedEmitJ is not always there when
> you are reading code that calls checkedEmitJ, so the ref and out
> parameters are useful in saving the reader time.

I'd still have to look up its declaration to understand exactly what it does.

> Making code easier to read is much more important than making it easier to write.
>

I don't know why this quote is repeated all the time. It is not like
ease of writing and ease of reading are decoupled or something.
What is most important is designing the language such that code
that is easy to write is easy to read.

What is easy to read is to some extent subjective. eg. I'd prefer to
just look at the called function's documentation rather than having
every call I later inspect redundantly repeat information present in
the function declaration. IMHO it's just noise.

> When I first saw:
> swap(ref a, ref b);
>
> I thought that looked ugly with the refs, but now I don't mind it. IMO
> it's only functions whose arguments are all passed by ref that don't
> really need annotation,

We are talking about call site annotations. Functions will always
require annotation to change the parameter passing style.

> and these are infrequent.

What is this claim based on? The use case above is probably one of the more common ones.

September 08, 2012
On Fri, 07 Sep 2012 13:34:02 +0200
"Kevin McTaggart" <kevin.mctaggart@drdc-rddc.gc.ca> wrote:

> I suggest that the language require ref and out when calling functions, as C# requires.

That's one thing I've always thought C# got better than D. I'd love to
see D fixed in this regard, and I've argued in favor of it
some time ago. Unfortunately I don't think it's going to happen.
Certainly not for D2 anyway, it's too late :(

September 09, 2012
On 08/09/2012 19:17, Timon Gehr wrote:
> On 09/08/2012 06:05 PM, Nick Treleaven wrote:
>> On 08/09/2012 14:05, Chris Nicholson-Sauls wrote:
>>> Given:
>>>
>>> void func (ref int[], int)
>>>
>>> If ref/out were required at the call site, this destroys UFCS.
>>>
>>> int[] array;
>>> array.func(0); // error, ref not specified by caller
>>
>> For UFCS, ref should be implied.
>
> Why? UFCS means uniform function call syntax.

I meant if callsite ref was required for parameters, it should not be required for array in 'array.func()'. It would be bizarre to require '(ref array).func()'.

>> Also for 'const ref' parameters, callsite ref should not be necessary.
>>
>
> The callee might escape a pointer to the argument. Which is
> 'non-obvious' as well when there is no callsite ref.

Knowing modification is more important to the local scope than knowing escapes, after all (in theory) the compiler should enforce that only data safe to be escaped is allowed to be escaped, at least in safe mode.

In any case, escaping is orthogonal to modification, so shouldn't use 'ref'.

>>> This suggestion has come up a couple times before, and each time failed
>>> to gain traction.
>>
>> But given that C, C#, (Go?) and Rust have the same philosophy that
>> argument modification should be explicit
>
> It is already explicit. The information is just not repeated at the
> call site.

It is not explicit at the call site. That is the crux of the argument for the feature.

>> for value types, it is arguably important.
>
> This is not necessarily a valid conclusion. Popularity does not imply
> importance.

I only said it was *arguably* important, given that the parent post said the idea failed to gain traction. Go and Rust are relatively new and decided for the explicit callsite approach (AFAIU).

>> It might be nice to have for D3.

BTW if this was desired (and it seems a long shot ATM) I was thinking there should be an automatic tool to upgrade code from D2 to D3.

>>> I wouldn't mind it as an option -- possibly even as a
>>> recommendation in most library code -- but as a requirement it honestly
>>> just gives me a headache.
>>
>> As an option it would serve no practical purpose other than
>> documentation.
>
> Requiring it makes lack of call site annotations carry some information.
> The only practical purpose would still be documentation.

Yes, albeit compiler enforced documentation.

>>> Generally speaking, if a parameter being
>>> ref/out is surprising, there is something wrong with the design.  (There
>>> are times it is non-obvious in otherwise good code, this seems
>>> uncommon.)
>>
>> Yes but unfortunately bad design is going to be a reality sometimes.
>
> Bad design never should be a motivation for adding language features.
> In fact, it seems to be a motivation for creating languages that have
> few features.

OK, but ref/out parameters aren't always a bad design.

>> Also there is a difference between 'surprising' and 'obvious'. Callsite
>> ref makes it quicker to understand code for a negligible cost,
>> occasionally a lot quicker if you're coming back to code you haven't
>> seen for a while.
>>
>
> IMHO it is better left to the future D editor. It can highlight ref/out
> arguments in a distinct style. Why clutter the language core even more
> with somewhat complex rules specifying in what cases call site ref is
> required?

1. Unix tools will never highlight ref/out parameters - e.g. grep.
2. Github probably won't ever.
3. Not everyone will use an editor that does so. Some people prefer a simple editor.

>> ...
>> The point is that the signature of checkedEmitJ is not always there when
>> you are reading code that calls checkedEmitJ, so the ref and out
>> parameters are useful in saving the reader time.
>
> I'd still have to look up its declaration to understand exactly what it
> does.

Sometimes you're not concerned with the precise effect of a function, you just want to work out how it impacts the surrounding code.

>> Making code easier to read is much more important than making it
>> easier to write.
>>
>
> I don't know why this quote is repeated all the time. It is not like
> ease of writing and ease of reading are decoupled or something.
> What is most important is designing the language such that code
> that is easy to write is easy to read.
>
> What is easy to read is to some extent subjective. eg. I'd prefer to
> just look at the called function's documentation rather than having
> every call I later inspect redundantly repeat information present in
> the function declaration. IMHO it's just noise.

It would likely become a motor-skill to ignore the keywords when you're not interested in them.

>> When I first saw:
>> swap(ref a, ref b);
>>
>> I thought that looked ugly with the refs, but now I don't mind it. IMO
>> it's only functions whose arguments are all passed by ref that don't
>> really need annotation,
>
> We are talking about call site annotations. Functions will always
> require annotation to change the parameter passing style.

Can't disagree there ;-)

>> and these are infrequent.
>
> What is this claim based on? The use case above is probably one of the
> more common ones.

How often do you use swap?

September 10, 2012
On 09/09/2012 02:54 PM, Nick Treleaven wrote:
> On 08/09/2012 19:17, Timon Gehr wrote:
>> On 09/08/2012 06:05 PM, Nick Treleaven wrote:
>>> On 08/09/2012 14:05, Chris Nicholson-Sauls wrote:
>>>> Given:
>>>>
>>>> void func (ref int[], int)
>>>>
>>>> If ref/out were required at the call site, this destroys UFCS.
>>>>
>>>> int[] array;
>>>> array.func(0); // error, ref not specified by caller
>>>
>>> For UFCS, ref should be implied.
>>
>> Why? UFCS means uniform function call syntax.
>
> I meant if callsite ref was required for parameters, it should not be
> required for array in 'array.func()'. It would be bizarre to require
> '(ref array).func()'.
>

Not more bizarre as in other positions.

>...
>>
>> This is not necessarily a valid conclusion. Popularity does not imply
>> importance.
>
> I only said it was *arguably* important, given that the parent post said
> the idea failed to gain traction. Go and Rust are relatively new and
> decided for the explicit callsite approach (AFAIU).
>

Yes, Go uses explicit pointer types.
Regarding Rust, you are wrong.
I have built the latest Rust compiler.

import io;

fn modify(&a:int){
    a = 2;
}
fn swap<T>(&a:T,&b:T){
   let tmp<-a;
   a<-b;
   b<-tmp;
}

fn main(){
   let mut a= 1;
   io::println(fmt!("%d",a)); // "1"
   modify(a);
   io::println(fmt!("%d",a)); // "2"
   let mut b=2, c=3;
   io::println(fmt!("%d %d",b,c)); // "2 3"
   swap(b,c);
   io::println(fmt!("%d %d",b,c)); // "3 2"
   let mut f = fn@(a:int)->int{ a + 1 };
   let mut g = fn@(a:int)->int{ a - 1 };
   io::println(fmt!("%d %d",f(2),g(2))); // "3 1"
   swap(f,g);
   io::println(fmt!("%d %d",f(2),g(2))); // "1 3"
}


> ...
>>
>> What is this claim based on? The use case above is probably one of the
>> more common ones.
>
> How often do you use swap?
>

Whenever I want to swap the values of two variables. All other
functions in my current project except one that use ref either pass by
const/immutable ref, or all arguments are ref, or they are function
pointer literals that are part of a polymorphic value type where it is
obvious that the first parameter is passed by ref. The remaining
function is private and declared right between the methods that use it.