May 04, 2021

On Tuesday, 4 May 2021 at 05:24:11 UTC, Imperatorn wrote:

>

On Tuesday, 4 May 2021 at 00:34:49 UTC, Q. Schroll wrote:

>

On Sunday, 2 May 2021 at 08:05:49 UTC, Imperatorn wrote:

>

Is there any way to enable this in the language?

auto a = [1,2,3] + [4,5,6]; //[5,7,9]

I want to allow the operation.

If I made a dmd fork, could I change this behavior "easily" or would it require a blood sacrifice

You could enable this easily using a wrapping struct template. Have a look at this: https://run.dlang.io/is/6CNgpy

In short, the solution is very elegant, very general, and only requires you to make two keystrokes .v before the operation. Alternatively, you can use v(1, 2, 3) instead of a slice literal.

Interesting! Even more cool if not the .v was required tho 😁

The operation necessitates an allocation and people here don't like hidden allocations.

May 04, 2021
On Tue, May 04, 2021 at 06:16:15PM +0000, Q. Schroll via Digitalmars-d wrote:
> On Tuesday, 4 May 2021 at 05:24:11 UTC, Imperatorn wrote:
> > On Tuesday, 4 May 2021 at 00:34:49 UTC, Q. Schroll wrote:
> > > On Sunday, 2 May 2021 at 08:05:49 UTC, Imperatorn wrote:
> > > > Is there any way to enable this in the language?
> > > > 
> > > > ```d
> > > > auto a = [1,2,3] + [4,5,6]; //[5,7,9]
> > > > ```
[...]
> > > Alternatively, you can use `v(1, 2, 3)` instead of a slice literal.
> > 
> > Interesting! Even more cool if not the .v was required tho 😁
> 
> The operation necessitates an allocation and people here don't like hidden allocations.

Why would an allocation be necessary?

	struct Vec(E, size_t n) {
		E[n] impl;
		alias impl this;

		E[n] opBinary(string op)(Vec v) {
			Vec result;
			mixin("result.impl[] = impl[] "~op~" v.impl[];");
			return result;
		}
	}
	auto v(Args...)(Args args) {
		import std.traits : CommonType;
		alias E = CommonType!Args;
		Vec!(E, Args.length) result;
		result.impl = [ args ];
		return result;
	}
	void main() @nogc {
		// Look, ma! No allocations!
		int[3] arr = v(1,2,3) + v(4,5,6);
		assert(arr[0] == 5 && arr[1] == 7 && arr[2] == 9);
	}


T

--
Some ideas are so stupid that only intellectuals could believe them. -- George Orwell
May 04, 2021

On Tuesday, 4 May 2021 at 18:53:18 UTC, H. S. Teoh wrote:

>

On Tue, May 04, 2021 at 06:16:15PM +0000, Q. Schroll via Digitalmars-d wrote:

>

On Tuesday, 4 May 2021 at 05:24:11 UTC, Imperatorn wrote:

>

On Tuesday, 4 May 2021 at 00:34:49 UTC, Q. Schroll wrote:

>

On Sunday, 2 May 2021 at 08:05:49 UTC, Imperatorn wrote:

>

Is there any way to enable this in the language?

auto a = [1,2,3] + [4,5,6]; //[5,7,9]

[...]

> > >

Alternatively, you can use v(1, 2, 3) instead of a slice literal.

Interesting! Even more cool if not the .v was required tho 😁

The operation necessitates an allocation and people here don't like hidden allocations.

Why would an allocation be necessary?

struct Vec(E, size_t n) {
E[n] impl;
alias impl this;

  E[n] opBinary(string op)(Vec v) {
  	Vec result;
  	mixin("result.impl[] = impl[] "~op~" v.impl[];");
  	return result;
  }

}
auto v(Args...)(Args args) {
import std.traits : CommonType;
alias E = CommonType!Args;
Vec!(E, Args.length) result;
result.impl = [ args ];
return result;
}
void main() @nogc {
// Look, ma! No allocations!
int[3] arr = v(1,2,3) + v(4,5,6);
assert(arr[0] == 5 && arr[1] == 7 && arr[2] == 9);
}

T

--
Some ideas are so stupid that only intellectuals could believe them. -- George Orwell

To the high priests of D (I have only written one project in D so far):

What would it take to allow array operations like I said before, without rewriting:

auto a = [1,2,3] + [3,2,1]; //[4,4,4]

Can this be accomplished using templates or a library solution, or do I have to modify the compiler?

Thanks

May 04, 2021

On Tuesday, 4 May 2021 at 19:44:24 UTC, Imperatorn wrote:

>

What would it take to allow array operations like I said before, without rewriting:

auto a = [1,2,3] + [3,2,1]; //[4,4,4]

Can this be accomplished using templates or a library solution, or do I have to modify the compiler?

C++ allows operator overloading for free-standing operator functions, D does not.

Adding this to the compiler is easy enough. You just rewrite all "+" operators to some internal __add(x,y) template and then default that back to "+", then overload it for anything you want... Clunky, but it should work?

May 04, 2021

On Tuesday, 4 May 2021 at 19:44:24 UTC, Imperatorn wrote:

>

On Tuesday, 4 May 2021 at 18:53:18 UTC, H. S. Teoh wrote:

>

On Tue, May 04, 2021 at 06:16:15PM +0000, Q. Schroll via Digitalmars-d wrote:

>

On Tuesday, 4 May 2021 at 05:24:11 UTC, Imperatorn wrote:

>

On Tuesday, 4 May 2021 at 00:34:49 UTC, Q. Schroll wrote:

>

On Sunday, 2 May 2021 at 08:05:49 UTC, Imperatorn wrote:

>

Is there any way to enable this in the language?

auto a = [1,2,3] + [4,5,6]; //[5,7,9]

[...]

> > >

Alternatively, you can use v(1, 2, 3) instead of a slice literal.

Interesting! Even more cool if not the .v was required tho 😁

The operation necessitates an allocation and people here don't like hidden allocations.

Why would an allocation be necessary?

struct Vec(E, size_t n) {
E[n] impl;
alias impl this;

 E[n] opBinary(string op)(Vec v) {
 	Vec result;
 	mixin("result.impl[] = impl[] "~op~" v.impl[];");
 	return result;
 }

}
auto v(Args...)(Args args) {
import std.traits : CommonType;
alias E = CommonType!Args;
Vec!(E, Args.length) result;
result.impl = [ args ];
return result;
}
void main() @nogc {
// Look, ma! No allocations!
int[3] arr = v(1,2,3) + v(4,5,6);
assert(arr[0] == 5 && arr[1] == 7 && arr[2] == 9);
}

To the high priests of D (I have only written one project in D so far):

What would it take to allow array operations like I said before, without rewriting:

auto a = [1,2,3] + [3,2,1]; //[4,4,4]

Can this be accomplished using templates or a library solution, or do I have to modify the compiler?

Array literals are internal to the compiler. But you can just use something different like v(1,2,3). Heck, you can get even closer using a struct with static opIndex to resemble the look-and-feel of array literals: v[1,2,3]. You can adapt H. S. Teoh's or my approach.

This is how it's done:

// for the array-literal-like syntax
struct v
{
    import std.traits : CommonType;
	
    static Vec!(CommonType!Args, Args.length) opIndex(Args...)(Args args)
    {
        return typeof(return)([args]);
    }
}

// the type of objects you create:
struct Vector(...) { ... } // by H. S. Teoh

void main() @nogc
{
    // Look, ma! No allocations!
    int[3] arr = v[1,2,3] + v[4,5,6];
    assert(arr[0] == 5 && arr[1] == 7 && arr[2] == 9);
}
May 04, 2021

On Tuesday, 4 May 2021 at 19:44:24 UTC, Imperatorn wrote:

>
auto a = [1,2,3] + [3,2,1]; //[4,4,4]

Can this be accomplished using templates or a library solution, or do I have to modify the compiler?

I'm by no means a high priest of D. However I'm fairly confident the answer to your question is no.

// Built-in:
int[3] a = [1,2,3] + [3,2,1]; // You can do this.
auto   a = [1,2,3] + [3,2,1]; // But not this.

// With H.S. Teoh's thing:
auto a = v(1,2,3) + v(3,2,1); // You can do this - a is int[3].
auto a = v(1,2,3) + v(3,2,1) + v(42,42,42); // But not this - Error: Invalid array operation.

So if you actually want to make this universal, you would need to modify the compiler.

From my own testing, the compiler actually turns these kinds of expressions into a function call.

int[3] a = [1,2,3] + [3,2,1];

// Gets turned into something like ..

import core.internal.array.operations;
int[3] a;
int[3] __temp1 = [1,2,3];
int[3] __temp2 = [3,2,1];
arrayOp!(int[], int[], int[], "+", "=")(a[], __temp1[], __temp2[], "+", "=");

This also might explain why you can't use auto a = ... (it's not a great excuse).

May 04, 2021
On Tue, May 04, 2021 at 09:49:16PM +0000, Blatnik via Digitalmars-d wrote: [...]
> ```
> // With H.S. Teoh's thing:
> auto a = v(1,2,3) + v(3,2,1); // You can do this - a is int[3].
> auto a = v(1,2,3) + v(3,2,1) + v(42,42,42); // But not this - Error: Invalid
> array operation.
> ```
[...]

The only reason it's invalid is because I made opBinary return E[n] rather than Vec!(E,n). If it returned Vec instead, then arbitrary expressions can be made possible (e.g., with generic overloads for opBinary and opUnary). With alias this, the Vec struct will become a thin wrapper over static arrays; `auto` will give you Vec!(E,n) rather than E[n], but any use of it that needs E[n] will automatically decay to E[n].

It's not 100% the same as built-in static array expressions, but it's pretty darned close.


T

-- 
He who does not appreciate the beauty of language is not worthy to bemoan its flaws.
May 05, 2021

On Tuesday, 4 May 2021 at 20:16:29 UTC, Ola Fosheim Grøstad wrote:

>

On Tuesday, 4 May 2021 at 19:44:24 UTC, Imperatorn wrote:

>

What would it take to allow array operations like I said before, without rewriting:

auto a = [1,2,3] + [3,2,1]; //[4,4,4]

Can this be accomplished using templates or a library solution, or do I have to modify the compiler?

C++ allows operator overloading for free-standing operator functions, D does not.

Adding this to the compiler is easy enough. You just rewrite all "+" operators to some internal __add(x,y) template and then default that back to "+", then overload it for anything you want... Clunky, but it should work?

Thanks everyone for all clarifications and examples! I'll experiment a bit and see what makes most sense.

May 05, 2021

On Saturday, 1 May 2021 at 11:32:47 UTC, Blatnik wrote:

>

I love how D redesigned the terrible C/C++ arrays. And it's awesome that array operations like element wise +, -, etc. are built into the language - it makes me want to use arrays and slices for everything. But there are still some rough edges in the design.

What do you expect to happen when you run this:

import std;
void main() {
  writeln(typeid([1, 2, 3]));
}

Maybe I'm crazy, but I would expect int[3] to be printed. But int[] gets printed instead.

Why? What's the reason for this design?

This automatic downconversion loses potentially useful type information by default. It also means lazy code like this:

auto array = [1, 2, 3]; // Type deduced as int[], not int[3]

performs an allocation, and thus it can't be used in @nogc or -betterC. (Not to mention that it's a completely unnecessary memory allocation).

It also means that functions that take a slice parameter:

void foo(int[] bar);

Can't be called naturally in @nogc or -betterC.

foo([1, 2, 3]) // Error: Array literal may cause GC allocation.

related : https://issues.dlang.org/show_bug.cgi?id=21756

>

Instead you have to do this:

...

May 05, 2021

On Wednesday, 5 May 2021 at 08:58:41 UTC, user1234 wrote:

>

On Saturday, 1 May 2021 at 11:32:47 UTC, Blatnik wrote:

>

I love how D redesigned the terrible C/C++ arrays. And it's awesome that array operations like element wise +, -, etc. are built into the language - it makes me want to use arrays and slices for everything. But there are still some rough edges in the design.

I have experimented some and found a few workarounds which might be acceptable.

But, I'm having some weird bug. On dmd and dmd-beta it works, but on dmd-nightly I get:

cannot interpret array literal expression [1, 2, 3] + [3, 2, 1] at compile time

I think this might be a regression?

Like, this works as I want:
https://run.dlang.io/is/rHRiP9

If I use dmd or dmd-beta. But if I change it to dmd-nightly it doesn't :(

😢😢😢