February 20, 2017
On Monday, 20 February 2017 at 14:54:58 UTC, Adam D. Ruppe wrote:
> On Monday, 20 February 2017 at 14:44:41 UTC, timmyjose wrote:
>> My confusion is this - the declaration of the array is arr [last-dimension]...[first-dimension], but the usage is arr[first-dimension]...[last-dimension]. Am I missing something here?
>
> I've never understood how anyone could actually like C's weird, backward way of doing arrays. It never made a lick of sense to me.

Hahaha! I suppose it's just a question of ingrained habit! :-)

> D is beautifully consistent: each index "peels off" a layer. If you had a function returning a function:
>
> void function(string) foo() {
>    return (string name) { writeln("hi, ", name); };
> }
>
> Is a zero-arg function that returns a function that takes a string parameter.
>
> How would you call the returned function?
>
> foo("adam")()
>
> or
>
> foo()("adam")
>
> ?
>
>
> Of course, the answer is the second form: the first level of () calls the function `foo`, which returns the function that takes the string parameter.
>
>
>
> Arrays are the same thing.
>
> int[2][3] arr;
>
> is a 3-element array of 2-element arrays of int. So, how do you get to the int[2]? You peel away a level of []:
>
> int[2] row = arr[0] // that peels away the [3], leaving an int[2]
>
> int a = row[0]; // peel away the last level, leaving just int
>
>

Yes, this does make sense!


> Beautifully consistent, even if you want pointers:
>
> int[2]*[3] arrOfPointers;
>
> arrOfPointers[0] // type int[2]*, aka "pointer to two-element array of int"
>
>
>
> And once you realize that opIndex can be overloaded, it makes even more sense:
>
>
> arr[1][0] gets rewritten to arr.opIndex(1).opIndex(0) - bringing us back to my first example, we almost literally have a function returning a function again. Of course it reads the other direction from declaration!

Okay, I don't understand all of it, but I can see your argument that it is more logically consistent this way.

February 20, 2017
On 02/20/2017 03:44 PM, timmyjose wrote:
> Things I don't like so much:
>
> 1). The std.range: iota function(?) is pretty nice, but the naming seems
> a bit bizarre, but quite nice to use.

Yeah, the name is weird. A little googling suggests it comes from C++ [1] which took it from APL.

> 2). The automatic conversion rules are nice for avoiding verbose code,
> but it looks like it might bite one just like in C++.

D at least disallows narrowing conversions. But yeah, conversions between signed/unsigned, from integral to floating point, or from narrower to wider char variants can have surprising results.

> 3). Not so much a fan of "auto", but it does have its uses, of course.

`auto` can obscure your code, but it can also make it more DRY. And with ranges and their combinations, types quickly get too complex to type out.

> 4). I'm still a bit confused by order of dimensions in rectangular arrays:
>
> Suppose I have a simple 2 x 3 array like so:
>
> import std.stdio;
> import std.range: iota;
>
> void main() {
>     // a 2 x 3 array
>     int [3][2] arr;
>
>     foreach (i; iota(0, 2)) {
>         foreach(j; iota(0, 3)) {
>             arr[i][j] = i+j;
>         }
>     }
>
>     writefln("second element in first row = %s", arr[0][1]);
>     writefln("third element in second row = %s", arr[1][2]);
>
>     writeln(arr);
> }
>
> My confusion is this - the declaration of the array is arr
> [last-dimension]...[first-dimension], but the usage is
> arr[first-dimension]...[last-dimension]. Am I missing something here?

You've got it. Declarations have the form `Type name;`. Fixed-size array types have the form `E[n]`. E can itself be another fixed-size array type, say F[m]. Then the whole type becomes F[m][n]. Simple.

The syntax could have be designed to grow in the other direction: [n]E = [n][m]F, to match indexing order. But Walter didn't make it that way.


[1] http://en.cppreference.com/w/cpp/algorithm/iota
February 20, 2017
On Monday, 20 February 2017 at 15:27:16 UTC, ag0aep6g wrote:
> On 02/20/2017 03:44 PM, timmyjose wrote:
>> Things I don't like so much:
>>
>> 1). The std.range: iota function(?) is pretty nice, but the naming seems
>> a bit bizarre, but quite nice to use.
>
> Yeah, the name is weird. A little googling suggests it comes from C++ [1] which took it from APL.

Damn! I'd just watched this interesting APL demo from 1975 a couple of days back
(https://www.youtube.com/watch?v=_DTpQ4Kk2wA) which does use iota, and I never did make the connection!

>> 2). The automatic conversion rules are nice for avoiding verbose code,
>> but it looks like it might bite one just like in C++.
>
> D at least disallows narrowing conversions. But yeah, conversions between signed/unsigned, from integral to floating point, or from narrower to wider char variants can have surprising results.
>
>> 3). Not so much a fan of "auto", but it does have its uses, of course.
>
> `auto` can obscure your code, but it can also make it more DRY. And with ranges and their combinations, types quickly get too complex to type out.

Absolutely agreed.

>> 4). I'm still a bit confused by order of dimensions in rectangular arrays:
>>
>> Suppose I have a simple 2 x 3 array like so:
>>
>> import std.stdio;
>> import std.range: iota;
>>
>> void main() {
>>     // a 2 x 3 array
>>     int [3][2] arr;
>>
>>     foreach (i; iota(0, 2)) {
>>         foreach(j; iota(0, 3)) {
>>             arr[i][j] = i+j;
>>         }
>>     }
>>
>>     writefln("second element in first row = %s", arr[0][1]);
>>     writefln("third element in second row = %s", arr[1][2]);
>>
>>     writeln(arr);
>> }
>>
>> My confusion is this - the declaration of the array is arr
>> [last-dimension]...[first-dimension], but the usage is
>> arr[first-dimension]...[last-dimension]. Am I missing something here?
>
> You've got it. Declarations have the form `Type name;`. Fixed-size array types have the form `E[n]`. E can itself be another fixed-size array type, say F[m]. Then the whole type becomes F[m][n]. Simple.

Brilliant! This explanation actually makes me get it now.

> The syntax could have be designed to grow in the other direction: [n]E = [n][m]F, to match indexing order. But Walter didn't make it that way.
>
>
> [1] http://en.cppreference.com/w/cpp/algorithm/iota


February 20, 2017
On 02/20/2017 06:44 AM, timmyjose wrote:

> 3). Not so much a fan of "auto", but it does have its uses, of course.

For completeness, D's 'auto' does not have the same meaning as C++'s auto. Wait... it actually has! :) But with the meaning of the 'auto' keyword from the olden C days: automatic storage class. (I correct this at the end.)

- C has 'auto', meaning "automatic storage class"

- 'auto' is redundant because it's the default anyway

- C++11 re-purposes 'auto' to mean automatic type inference (Ali and others initially assume 'auto' is the same in D.)

- D has automatic type inference universally without special keyword:

    const i = 42;    // No auto

- D needs a place holder to satisfy its syntax rules in certain cases

    i = 42;    // Definition or a typo? Compilation error!

- D brings back the original meaning of 'auto':

    // Yes, automatic storage class but now everybody's happy
    auto i = 42;

Correction: It's actually the 'auto attribute' in D with the venerable responsibility of "The auto attribute is used when there are no other attributes and type inference is desired." Good job, auto! :o)

Ali

February 20, 2017
On Mon, Feb 20, 2017 at 03:00:05PM +0000, timmyjose via Digitalmars-d-learn wrote: [...]
> Just one question about the compilers though - I read on the Wiki that there are three main compiler distros - dmd, ldc, and gdc. I code primarily on a mac, and I have installed both dmd and ldc. A lot of the flags appears to be similar, and for my small programs, compilation and execution speed appeared to be almost identical. However, the book suggested using dmd for dev and probably ldc/gdc for releases. Is this really followed that much in practice, or should I prefer dmd?

Personally, I use dmd git HEAD for 90% of my D projects, because (1) I'm such a sucker for the latest and greatest features, bugfixes, language changes, etc., and (2) I occasionally contribute to the compiler toolchain (mainly Phobos, sometimes druntime or dmd itself) and it's much easier to debug something I use on a regular basis and not have to switch to a different version or waste time chasing down a compiler bug that's already been fixed in git HEAD.

However, when I need performant code, I pull up my trusty, rusty old gdc (which, unfortunately, tends to be about a version or two behind the main dmd release -- I believe Iain is working on improving this, though). In spite of Walter being a renowned compiler genius, he simply has too many things on his plate and working on the optimizer hasn't been a high priority, so gdc's optimizer easily beats dmd (sometimes by a long stretch).  Don't get me wrong; for your average desktop application, dmd output is more than good enough. It only really matters when you're dealing with CPU-intensive performance-critical things like maintaining framerate in a complex game engine, or real-time software where somebody dies if the program fails to respond within a 10ms margin, or when you're trying to solve a PSPACE-complete exponential problem where a 20ms difference in inner loop performance could mean the difference between getting a result next week vs. next year (or next millenium).

But if you're a stickler for high-performance code, gdc's optimizer far outstrips dmd's in just about every way that I can think of -- more aggressive inlining, better loop optimization, better codegen in general.  And reputedly ldc has comparable performance gains over dmd as well, so that's another option.  The only downside is that gdc releases are tied to the gcc release cycle, so it tends to be about a version or two behind mainline dmd, and ldc is about a version behind AFAIK.  But as far as the basics of D are concerned, that shouldn't make a big difference, unless you're unlucky enough to be bitten by a compiler bug that has no workaround and that's only fixed in the latest dmd release. Thankfully, though, compiler bugs of that sort have been quite rare (and getting rarer with recent releases).


> One more thing I noticed when I looked into the executable file (using "nm -gU" on my mac) is that I found two interesting symbols - _main and _Dmain.  On Rust, for instance, the main function got turned into _main, so I couldn't use a main in the C code that I was trying to interop with from my Rust code. In this case, does the same restriction apply (I am still way too far from dabbling in interop in D as yet! :-)). I mean, suppose I write some sample code in C, and I have a local main function to test the code out locally, will I have to comment that out when invoking that library from D, or can I keep that as is?

_Dmain is the entry point of your D program, and is only emitted if you have a main() function in your D code.  In that case, you'll want the druntime version of _main (which does a bunch of setup necessary before _Dmain is called).  But if you're calling D from C code, i.e., the C code defines main(), then you wouldn't also write a main() in D code (obviously -- I hope), though you *would* need to call a druntime hook to initialize some D runtime things needed before you call any D code. (Sorry, can't remember the exact calls off the top of my head, but it's a simple matter of calling an init routine or two at startup, before invoking any D code, then calling the cleanup routine at the end before the program exits. Pretty standard stuff.)


T

-- 
Береги платье снову, а здоровье смолоду.
February 20, 2017
Ali Çehreli wrote:

> Correction: It's actually the 'auto attribute' in D with the venerable responsibility of "The auto attribute is used when there are no other attributes and type inference is desired." Good job, auto! :o)

 foreach (auto n; arr)

oops. good job, auto!
February 20, 2017
On 02/20/2017 07:00 AM, timmyjose wrote:

> slice can be spawned off into a brand new array upon assigning data to
> it (in the book "Learning D", which I find very nice so far).

It's not assigning data to a slice, but adding elements to it: It *may* spawn off a new array. You can use .capacity to see whether that will be the case:

  http://ddili.org/ders/d.en/slices.html#ix_slices..capacity

Related to your earlier question on multi-dimensional array syntax, which you seem to find "brilliant": :)


http://ddili.org/ders/d.en/slices.html#ix_slices.multi-dimensional%20array

Also, there is the following article which explains the inner workings of slices:

  https://dlang.org/d-array-article.html

Ali

February 20, 2017
On Mon, Feb 20, 2017 at 05:39:30PM +0000, ketmar via Digitalmars-d-learn wrote:
> Ali Çehreli wrote:
> 
> > Correction: It's actually the 'auto attribute' in D with the venerable responsibility of "The auto attribute is used when there are no other attributes and type inference is desired." Good job, auto! :o)
> 
>  foreach (auto n; arr)
> 
> oops. good job, auto!

Haha... in this case you want to actually just drop `auto` completely. :-D

But yeah, there are some funny inconsistencies in what exactly `auto` is supposed to mean.  Hard to say whether this case should be considered a bug, though.  In any case, it's only a trivial one.


T

-- 
"I'm running Windows '98." "Yes." "My computer isn't working now." "Yes, you already said that." -- User-Friendly
February 21, 2017
"H. S. Teoh via Digitalmars-d-learn" wrote:

> On Mon, Feb 20, 2017 at 05:39:30PM +0000, ketmar via Digitalmars-d-learn wrote:
>>  foreach (auto n; arr)
>>
>> oops. good job, auto!
>
> Haha... in this case you want to actually just drop `auto` completely.
> :-D
>
> But yeah, there are some funny inconsistencies in what exactly `auto` is
> supposed to mean.  Hard to say whether this case should be considered a
> bug, though.  In any case, it's only a trivial one.
this issue (`auto` in `foreach`) is raising from time to time, so it is definitely a sign that something is wrong. but i won't go further, it was already beaten to death zillion times.
February 21, 2017
On Saturday, 18 February 2017 at 20:15:55 UTC, timmyjose wrote:
> 2. I am more interested in learning D as a pure systems programming language so that I can develop my own tools (not looking to develop an OS, just some grep-scale tools to start off with). In that regard, I have a few concerns about the GC. My rudimentary knowledge of the D ecosystem tells me that there is a GC in D, but that can be turned off. Is this correct? Also, some threads online mention that if we do turn off GC, some of the core std libraries may not fully work. Is this presumption also correct?
>
> In this regard, I am curious to know if I would face any issues (with my intent in mind), or will I do just fine? If you could share your experiences and domains of use, that would also be very helpful for me. Secondly, how stable is the language and how fast is the pace of development on D?
>
> Again, sorry for my ignorance if I have been wrong-footed on some (or all) points.

I'm using D for small tools for about a year now and I never had to mess with GC. Most of the tools don't need to work on GBs of data and performance has always been good enough.

My "biggest" D tool is a custom scriptable code generator based on lua and sdl (sdlang.org) and even though it's implemented really badly, it performs good enough to be used in development (Currently we generate JSON serialization code for delphi with it).

I also wrote a simple parser for parsing delphi memory leak reports to show some statistics. Depending on how many leaks you have, these can get a bit larger, but I always got good enough performance with D.

Last tool I want to mention is a binary log file parser, which reads an proprietary log file and converts it into json. And even this is extremely fast.

So I don't think GC will be a big problem for smaller tools.