Re: Best practices of using const

2019-06-22 Thread Yatheendra via Digitalmars-d-learn

On Saturday, 22 June 2019 at 05:10:14 UTC, Yatheendra wrote:
It feels disingenous to want to call a caching object even 
"logically" const. There has to be a scaffolding-based but 
hopefully generic compromise. I haven't yet tested this belief, 
but I believe "physical" const is of good use wherever it can 
be applied.


On Friday, 21 June 2019 at 23:39:20 UTC, H. S. Teoh wrote:

The problem with this is that you cannot use const(Wrapper).

In particular, if you have a function that wants to document 
that it does not mutate its argument, you cannot write:


auto func(in Wrapper data) { ... }

because const(Wrapper) does not allow lazy initialization.
...

...


"physical" const has to be applicable & good in many/most other 
use-cases than caching (citation needed). Somehow, wanting to 
call mutating code on logically const values sounds to be the 
wrong want.


Lazy initialization sounds like it will be a good DIP :-) 
Generate (once) on (first) read, just like copy (once) on (first) 
write. But there are other ways.


Heavy computation called at most once: bite the bullet, eagerly 
construct an immutable value ahead of time. "physical" const 
might have just enough optimization opportunity to offset biting 
the bullet.


Called more than once: same thing.


Re: Best practices of using const

2019-06-21 Thread Yatheendra via Digitalmars-d-learn
It feels disingenous to want to call a caching object even 
"logically" const. There has to be a scaffolding-based but 
hopefully generic compromise. I haven't yet tested this belief, 
but I believe "physical" const is of good use wherever it can be 
applied.


On Friday, 21 June 2019 at 23:39:20 UTC, H. S. Teoh wrote:

The problem with this is that you cannot use const(Wrapper).

In particular, if you have a function that wants to document 
that it does not mutate its argument, you cannot write:


auto func(in Wrapper data) { ... }

because const(Wrapper) does not allow lazy initialization.
...


IMHO, in parameters are a more important scenario than const in 
ranges (of course, same constraints).


Just for the heck of it, I'll try to get a snippet "working" but 
I see out parameters snaking all through the call chain!


Re: Best practices of using const

2019-06-21 Thread Yatheendra via Digitalmars-d-learn
That is a comprehensive reply. No pointers to other material 
required :-)


On Friday, 21 June 2019 at 16:35:50 UTC, H. S. Teoh wrote:
On Fri, Jun 21, 2019 at 06:07:59AM +, Yatheendra via 
Digitalmars-d-learn wrote:
Actually, optimizers work best when there is minimal mutation 
*in the original source*.  The emitted code, of course, is free 
to use mutation however it wants.  But the trouble with 
mutation at the source level is that it makes many code 
analyses very complex, which hinders the optimizer from doing 
what it might have been able to do in the absence of mutation 
(or a reduced usage of mutation).

[...]


(aside: I hope we don't end up advocating the Haskell/Erlang way 
or the Clojure way!)


Yes, the hindrances of non-const code are documented (are most 
programmers listening!). I was only pointing out that mutation 
being part of the design limits what can be logically const. Is 
the trade-off clear, between (mythical) guaranteed C++-like-const 
at all the points we remember to put it, versus guaranteed 
D-const at the fewer points we manage to put it? Does D-const 
distort the design (but you get all the optimizations possible in 
that scenario)?


The inability to have a const caching object seems correct. 
The way around would be to have a wrapper that caches (meh). 
If that is not possible, then maybe caching objects just 
aren't meant to be const by their nature? Isn't memoize a 
standard library feature? I should look at it, but I wouldn't 
expect it to be const.


It's not as simple as it might seem.  Here's the crux of the 
problem: you have an object that logically never changes 
(assuming no bugs, of course).  Meaning every time you read it, 
you get the same value, and so multiple reads can be elided, 
etc.. I.e., you want to tell the compiler that it's OK to 
assume this object is const (or immutable).


However, it is expensive to initialize, and you'd like it to be 
initialized only when it's actually needed, and once 
initialized you'd like it to be cached so that you don't have 
to incur the initialization cost again.  However, declaring a 
const object in D requires initialization, and after 
initialization it cannot be mutated anymore. This means you 
cannot declare it const in the first place if you want caching.


It gets worse, though.  Wrappers only work up to a certain 
point.  But when you're dealing with generic code, it becomes 
problematic.  Assume, for instance, that you have a type Costly 
that's logically const, but lazily initialized (and cached).  
Since you can't actually declare it const -- otherwise lazy 
initialization doesn't work -- you have to declare it mutable.  
Or, in this case, declare a wrapper that holds a const 
reference to it, say something like this:


struct Payload {
// lazily-initialized data
}

struct Wrapper {
const(Payload)* impl;
...
}

However, what if you're applying some generic algorithms to it?
 Generic code generally assume that given a type T, if you want 
to declare a const instance of it, you simply write const(T).  
But what do you pass to the generic function? If you pass 
Wrapper, const(Wrapper) means `impl` cannot be rebound, so 
lazily initialization fails.  OK, then let's pass 
const(Payload) directly.  But that means you no longer have a 
wrapper, so you can't have lazy initialization (Payload must be 
constructed before you can pass it to the function, thus it 
must be eagerly initialized at this point).




I should check on std memoize & maybe code something up for 
understanding before writing more than this - would you mind 
pointing to an example range algorithm that we would have trouble 
passing a caching wrapper to?


I hadn't considered pointers as an option. Why wouldn't the 
following work, if expressible in D?

   struct CostlyComputeResult {
  ... // data fields
  // constructor takes compute results, no postblit
   }

   struct Wrapper {
  const (CostlyComputeResult) *cachee = 0;
  ... // data fields storing compute inputs
  // constructor takes compute inputs
  // pointer to function(compute inputs)
  const ref get() {
 if (!cachee) {
cachee = new(function(inputs));
 }
 return cachee;
  }
   }

Hopefully jumping through these hoops is worth the while. 
Instead, maybe just wait until the compiler grows a 'cache pure' 
function qualifier (move constructor required?).


Re: Best practices of using const

2019-06-21 Thread Yatheendra via Digitalmars-d-learn

Am I mistaken in saying that we are conflating:
   "anything that is logically const should be declared const"
   // makes perfect sense
   // e.g. the lowest 2, and some branches of the 3rd and 4th, 
levels
   // of members (and a subset of the overall methods) in a 
5-deep type hierarchy are const

with:
   "most code/data should be declared const"
   // no! isn't efficient code all about mutation?
   // no grounds for, e.g.: "ideally, no more than 40% of code 
should be doing mutation"


On Wednesday, 13 February 2019 at 16:40:18 UTC, H. S. Teoh 
wrote:
On Wed, Feb 13, 2019 at 11:32:46AM +, envoid via 
Digitalmars-d-learn wrote:
Unfortunately, that guarantee also excludes a lot of otherwise 
useful idioms, like objects that cache data -- a const object 
cannot cache data because that means it's being mutated, or 
lazily-initialized objects -- because once the ctor has run, 
the object can no longer be mutated. Most notably, D's 
powerful range idiom is pretty much unusable with const 
because iteration over a range requires mutating the range 
(though having const *elements* in a range is fine).  This 
doesn't seem as bad at first glance, but it wreaks havoc on 
generic code, another thing that D is purportedly good at. 
It's very hard (and often impossible) to write generic code 
that works with both const and mutable objects.


So ironically, the iron-clad semantics of D's const system 
turns out to be also its own downfall.



T


The point about generic code (reiterated by many) is intriguing 
on its own; until now, I hadn't explicitly thought about const 
even for my C++ template library code (whatever little I have of 
those). Any pointers to other posts or articles elaborating this 
a little bit?


I believe the other points probably matter when interacting with 
every other feature (I would have to write some of my "real" code 
in D to see if I hit it on my own), but there doesn't seem to be 
anything unusable about them on their own.


The inability to have a const caching object seems correct. The 
way around would be to have a wrapper that caches (meh). If that 
is not possible, then maybe caching objects just aren't meant to 
be const by their nature? Isn't memoize a standard library 
feature? I should look at it, but I wouldn't expect it to be 
const.


On Monday, 18 February 2019 at 06:50:32 UTC, Marco de Wild wrote:


I agree that const by nature unfortunately kills lazy 
initialization.


Lazy initialization - is this the same as post-blit? At the cost 
of copying (justifiable? maybe), doesn't D have a way to 
copy-construct a const/immutable struct object from a mutable 
one? If there is a way (or will be - there is a recent posting 
and a Dconf talk about copy constructors), does the copying 
negate the benefits of lazy initialization?


However, I don't really understand why const is a problem with 
ranges. Const elements are not a problem. Iterating over a 
range consumes it (if I understand correctly). It does not make 
sense to be able to consume a const object, so from my point of 
view it's perfectly logical to disallow iterating const ranges. 
If I'm missing something, please correct me.

...


+1.

Or I haven't understood why ranges would ever ever need to be 
const.

After all, in C++, what use is:
   std::vector::const_iterator const iter = sequence.begin();
About the only kind of use would be:
   std::vector::const_iterator iter = sequence.begin();
   std::vector::const_iterator const iterEnd = sequence.end();
What are ranges if not an encapsulation of the above 
functionality?




Re: Component based programming in D

2019-06-18 Thread Yatheendra via Digitalmars-d-learn
I guess design patterns are independent of implementation 
language. Anyway, I suspect OO is incidental in components. D 
probably can do it better than C++ at least. Do search for the 
video of a talk Walter Bright gave on component programming in D.


System requirements for compiling/linking big D codebases

2019-06-17 Thread Yatheendra via Digitalmars-d-learn

Hi,

For the sake of people habituated to compiling compilers from 
source, is it possible to add to the dmd_linux page the 64-bit 
hardware resource requirements for compiling DMD/Phobos from 
source? Ideally, with the defaults and also with 2.086's -lowmem 
switch? Maybe an ever-green link, to those numbers seen on the CI 
infrastructure when building the release?


I hope it is not out of the way for users to compile DMD from 
source, since I guess people do it for Rust, OCaml, etc. all the 
time (and, compiling GCC in stages isn't rare either). -lowmem 
hopefully will make the compiler's compilation memory usage as 
palatable as other compilers'.


I have had to incompletely discover this info. What I have seen 
is that compiling takes maybe 1G disk dpace, linking DMD doesn't 
even take 1G of RAM, whereas 3+G available RAM isn't enough for 
linking Phobos. Short of waiting for Gentoo's dlang to move on 
from 2.085 so that I have recourse to -lowmem, I would have to 
chuck more RAM into an older system that actually compiles GCC, 
libstdc++, OCaml, etc. just fine.


- Cheers.


Re: OT (Was: Re: is(ElementType!(char[2]) == dchar - why?)

2019-06-14 Thread Yatheendra via Digitalmars-d-learn
Could this be rendered an aside for newbies, by way of 
documentation, specifically the Unicode portion of the Dlang 
tour? Just never bring up auto-decoding at all, point out 
UTF8/16/32 and point out the fast correct primitive (byCodeUnit) 
that lets you iterate over a string's contents simplistically as 
in other languages. Leave the "proper" handling of Unicode to 
content outside of the tour.


I know very little about Unicode/UTF. If I had only read about 
byCodeUnit, I wouldn't be bothered at all because I understand 
Hindi (and Mandarin, and Japanese, Russian too?) is very 
different from European languages & needs explicit care for 
proper handling. I would be very glad that D makes the situation 
clear, and provides the zero-barrier API to start newbies off on 
an equal footing with past languages.


Re: 'version'-based code selection

2019-06-02 Thread Yatheendra via Digitalmars-d-learn

On Saturday, 1 June 2019 at 07:46:40 UTC, Jonathan M Davis wrote:


Like static ifs, version statements are completely a 
compile-time construct and having nothing to do with runtime 
beyond how they affect the code that's generated.

...
- Jonathan M Davis


Thanks for taking the time.

That's it then for selecting between malloc/free implementations 
at runtime (program start-up time, usually) in such a way. There 
might still be hope in the form of a separate dynamic library 
with re-implemented malloc, and equivalent wrapper API's around 
libc in another library (whole program, including Phobos, relying 
on the same malloc library).


'version'-based code selection

2019-05-31 Thread Yatheendra via Digitalmars-d-learn



Hi people.

The 'version' keyword sounds like a fantastic capability, but how 
far does DMD take it (and does GDC take it equally far)? This is 
not a "D Improvement Proposal", I am just asking how it is now.


Can code of multiple versions be compiled into the same 
executable or library, and a particular one selected from 
"later"? I guess not without some name mangling and wrangling.


Can such selection be done at link-time for standard as well as 
user-defined library code? Maybe some library file naming 
conventions are followed to allow the compiler to select the 
corresponding file?


Wishful thinking, but can the selection be at runtime? That would 
be language support for making some things easy, e.g. picking 
from assembly-coded routines based on runtime CPU id (the video 
player MPlayer picks routines that way, and I read that the D 
library, Mir, has CPU id support).


Thanks.

P.S: I am a brand-new C++'y asylum seeker who may have missed 
seeing some documentation, so RTFM is a valid response.


P.P.S: This question was triggered by the D GSoC project for 
independent-of-C implementations of malloc/free/memcpy/memset. 
Could a common malloc be exposed to the D 
runtime/Phobos/programs, with the C or D implementations 
selectable at link-time (using a mechanism available to user code 
too)?