Thread overview
Some thoughts about C and D, return data through parameters
Dec 19, 2017
cosinus
Dec 21, 2017
Nick Treleaven
Dec 19, 2017
ketmar
Dec 19, 2017
ketmar
December 19, 2017
Recently I've asked my self why `C` isn't capable of returning multiple values at once. And I thought that the return-statement was primarally used only for error-handling and all the valuable data has been returned through the parameter-list.
If this was true then all `C`-like languages had abused the return-statement till  now.

This is the way most programmers are doing it:

```C
int add(int a, int b);
// ...
int c = add(a, b);
```

This is the way I think `C` was designed to:

```C
int add(int *c, int a, int b);
// ...
int c;
if(add(&c, a, b)) {
    printf("error!");
}
```

This isn't good example but think about how you are doing it with huge structs or even arrays.

I think the main reason why most people are using the first example is because it looks more like a function in math or you need less code to call the function or we think the parameter-list is for inputs only.
But the second one is faster especially with huge junks of data. I think a lot of unnecessary allocations has been done just to be able to call the function like the first example. Think about `std::string` in c++.

So my question is would it be a good idea to have some kind of implicit declarations of variables that are used as parameters:

```D
int add(decl ref int c, int a, int b);

// ...

// c is unknown here

if(add(c, 123, 456)) {
    writeln("error!");
}
// c is implicit declared at the function-call above.
assert(c == 579);
```

The good things out of this are:

* The function becomes easier to be called
* The variable `c` does not need to be default-initialized to keep it save
* It's like the `auto`-declaration we can already use to declare a variable that keeps the return-value
* It solves the problem with multiple return-values.

A second thought that came up was:
Shouldn't there be a compiler-error if someone is ignoring the return-value of a function?

I saw this C-code:

```C
(void)printf("Hello World!");
```

It cast's the return-value to void to tell the compiler and other programmer's that the return-value can be ignored.
December 19, 2017
On Tuesday, 19 December 2017 at 01:01:57 UTC, cosinus wrote:
> Recently I've asked my self why `C` isn't capable of returning multiple values at once. And I thought that the return-statement was primarally used only for error-handling and all the valuable data has been returned through the parameter-list.
> If this was true then all `C`-like languages had abused the return-statement till  now.
>
> This is the way most programmers are doing it:
>
> ```C
> int add(int a, int b);
> // ...
> int c = add(a, b);
> ```
>
> This is the way I think `C` was designed to:
>
> ```C
> int add(int *c, int a, int b);
> // ...
> int c;
> if(add(&c, a, b)) {
>     printf("error!");
> }
> ```
>
> This isn't good example but think about how you are doing it with huge structs or even arrays.
>
> I think the main reason why most people are using the first example is because it looks more like a function in math or you need less code to call the function or we think the parameter-list is for inputs only.
> But the second one is faster especially with huge junks of data. I think a lot of unnecessary allocations has been done just to be able to call the function like the first example. Think about `std::string` in c++.
>
> So my question is would it be a good idea to have some kind of implicit declarations of variables that are used as parameters:
>
> ```D
> int add(decl ref int c, int a, int b);
>
> // ...
>
> // c is unknown here
>
> if(add(c, 123, 456)) {
>     writeln("error!");
> }
> // c is implicit declared at the function-call above.
> assert(c == 579);
> ```
>
> The good things out of this are:
>
> * The function becomes easier to be called
> * The variable `c` does not need to be default-initialized to keep it save
> * It's like the `auto`-declaration we can already use to declare a variable that keeps the return-value
> * It solves the problem with multiple return-values.
>
> [snip]

Reminds me of C#7's out variable declarations: https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-7#out-variables

However multiple return values are much better implemented through language-integrated tuples: https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-7#tuples
December 19, 2017
On Tuesday, 19 December 2017 at 01:01:57 UTC, cosinus wrote:
> [snip]
>
> A second thought that came up was:
> Shouldn't there be a compiler-error if someone is ignoring the return-value of a function?
>
> I saw this C-code:
>
> ```C
> (void)printf("Hello World!");
> ```
>
> It cast's the return-value to void to tell the compiler and other programmer's that the return-value can be ignored.

Ignoring the return value is mainly useful when the primary use of the function are it's side effects, not it's return value. In many languages derived from C, people prefer using 'void' return types + exceptions for error handling.

It is an error to ignore the result of function with no side effects (D models such functions with the 'pure' attribute): https://dlang.org/spec/function.html#pure-functions
December 19, 2017
cosinus wrote:

> So my question is would it be a good idea to have some kind of implicit declarations of variables that are used as parameters:
>
> ```D
> int add(decl ref int c, int a, int b);
>
> // ...
>
> // c is unknown here
>
> if(add(c, 123, 456)) {
>      writeln("error!");
> }
> // c is implicit declared at the function-call above.
> assert(c == 579);
> ```

D already has one such construct, `foreach`. and it is a huge misdesign. it is *absolutely* not clear from the code that `c` is introduced by a function call. exactly like with `foreach (c; ...)`, any sane person expects that just using variable name without any type prepended means "let's use already declared variable `c`". the ONLY place where this uniformity is broken now is `foreach`, and this is already bad.

note that D devs stubbornly insists that `auto` is not a type placeholder, but a storage qualifier, like `static`, so you can't write `foreach (auto c; ...)` (and compiler rejects such code). yet, `if (auto c = ...)` is perfectly allowed, and here `auto` is type placeholder. talking about consistency, yeah.

anyway, adding yet another broken promise (aka illogical inconsistency) into language won't do any good. each inconsistency makes code harder to read and understand.
December 19, 2017
On 12/19/17 6:27 AM, ketmar wrote:

> note that D devs stubbornly insists that `auto` is not a type placeholder, but a storage qualifier, like `static`, so you can't write `foreach (auto c; ...)` (and compiler rejects such code). yet, `if (auto c = ...)` is perfectly allowed, and here `auto` is type placeholder. talking about consistency, yeah.

I insist that auto is a storage qualifier, AND I think it should be allowed as the storage qualifier for the foreach variable.

foreach(ref c; ...) works. I don't see why foreach(auto c; ...) shouldn't.

-Steve
December 19, 2017
Steven Schveighoffer wrote:

> I insist that auto is a storage qualifier

what is the rationale to have `auto` as storage qualifier? except of keeping it in line with the ancient C feature (and most C programmers don't even know what `auto` does in C).

i bet that most people are sure that `auto` in D is a type placeholder. it works like type placeholder, it looks like type placeholder, why don't make it a type placeholder?

ok, i can see *one* place where it won't be consistent: `foo (auto ref Type v)`. which can be left as a logical exception ('cmon, `enum a = 20;`) is not declaring an enum too, it is used to declare "inline constant".

also:

	auto int n = 42;

yay: "Error: variable n storage class 'auto' has no effect if type is not inferred, did you mean 'scope'?"

even D compiler knows that `auto` is used as *type* *placeholder*. 'cmon, let's promote it to actual type placeholder!
December 19, 2017
On 12/19/17 9:35 AM, ketmar wrote:
> Steven Schveighoffer wrote:
> 
>> I insist that auto is a storage qualifier
> 
> what is the rationale to have `auto` as storage qualifier? except of keeping it in line with the ancient C feature (and most C programmers don't even know what `auto` does in C).

Because it's consistent. D says that types are inferred when the type is missing and there is a storage class specified. Making auto a storage class fits this definitely perfectly.

> 
> i bet that most people are sure that `auto` in D is a type placeholder. it works like type placeholder, it looks like type placeholder, why don't make it a type placeholder?

It could be specified that way, but then it's another thing to explain, which doesn't add any value. The spec can read "The type of a variable is inferred from the initializer if you specify a storage class" or it can read "The type of a variable is inferred from the initializer if you specify a storage class, or if you use 'auto' as the type placeholder." I don't see a gain there.

> ok, i can see *one* place where it won't be consistent: `foo (auto ref Type v)`. which can be left as a logical exception ('cmon, `enum a = 20;`) is not declaring an enum too, it is used to declare "inline constant".

auto ref, while a nice feature, is a horrible syntax. It's an exception no matter what we do!

> 
> also:
> 
>      auto int n = 42;
> 
> yay: "Error: variable n storage class 'auto' has no effect if type is not inferred, did you mean 'scope'?" >
> even D compiler knows that `auto` is used as *type* *placeholder*. 'cmon, let's promote it to actual type placeholder!

Another inconsistency that should be fixed (and allowed).

-Steve
December 21, 2017
On Tuesday, 19 December 2017 at 08:54:38 UTC, Petar Kirov [ZombineDev] wrote:
> Reminds me of C#7's out variable declarations: https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-7#out-variables
>
> However multiple return values are much better implemented through language-integrated tuples: https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-7#tuples

In D we don't have tuple unpacking syntax, but if we supported inline variable declarations for out arguments, we could do it with a library function:

void unpack(T, A...)(T tuple, out A args);
tuple(4, false).unpack(auto first, auto second);