Hacker News new | ask | show | jobs
by gitgud 1663 days ago
Interesting language

    // Increment all elements by one (array-wise expression)
    half[] += 1;
Is this really that useful? Seems like a weirdly specific code smell...
5 comments

I don't know D and just learning this syntax myself, and I'm genuinely interested in where you feel the smell is because I really like this syntax! For example:

I know Ruby, so here is what I'm used to:

  half.map { |i| i + 1 }
I also know Elixir:

  Enum.map(half, fn i -> i + 1 end)
...and JS, I guess because we have to:

  half.map(i => i + 1) // It looks just like the Ruby one
...vs whatever the Python version is... I don't know Python very well, but I imagine it involves list comprehensions...

Compare all those to the aforementioned D version :

  half[] += 1;
I'm really feeling the D version!

EDITED to fix little mistakes.

All of those create a copy of the array and do not modify the original, which may be important on cases like audio manipulation, where there are for instance 44,000 array entries per second. Furthermore, I wonder if D could automatically vectorize this operation when SSE/MMX style instructions are available?
It's not D that auto-vectorize such expression, it's the backend. Those "array ops" makes it easier for the compiler to convey that to the backend, though. Using LDC + arrays ops is often the best thing you can do speed-wise.
Sorry I should have been more clear. Incrementing every element in an array is much less useful than ".map()" which can execute a function on every element.

It just seems like a weirdly specific syntactic sugar... which is a "language smell"... to me

You can use arrays as if they were individual operands, and it will expand out the loop and apply the expression to all the values (and can use optimization/vector tricks if posssible).

e.g.:

arr1[] += arr2[] / 10.0 + 5;

TBH, I don't use this feature much, because I work with ranges more than arrays, which do not have this ability. This feature predates ranges (and the std.algorithm.map function, which can do what you say as well).

Well you can also just call .map if you want. The nice thing about the [] operation is the compiler can optimize it more heavily since it is restricted to simpler instructions.
Yes it is useful, because "array ops" auto-vectorize well and you can write complex function that are vectorized. It's a bit like expression templates but with compiler support. Typically if something can be written as an array op expression it isn't worth rewriting with intrinsics or assembly. For me it's a killer feature :)

You can also mix and match scalar and slices, so for audio code it's quite cool:

    a[0..frames] += b[0..frames] * volume;
Loops are a code smell ;)

In this case the syntax lowers to a template which handles the iteration for you. It is not special, it gets treated the same by the compiler backend, but it will almost definitely be inlined & vectorized if the compiler is allowed to.

GCC and LLVM are actually clever enough to call memset for you if they deem it profitable.

It can be a bit more general https://dlang.org/spec/arrays.html#array-operations

Looking at the notes, it may have separate syntax to make compiler vectorization and parallelization easier. Plus it's pretty terse.

Wish I had this in JS tbh