On Fri, Feb 09, 2018 at 01:36:50PM -0800, Manu wrote:
>    On 9 February 2018 at 11:19, H. S. Teoh via Digitalmars-d
>    <[1]digitalmars-d@puremagic.com> wrote:
>      >    3. string mixins always used in place of some sort of more
>      >sanitary     macro system
>      [...]
>      That gave me a double-take.  "Sanitary" and "macro" in the same
>      sentence?!  That's just ... I know what you *mean*, but the
>      thought is just, wow. :-D
>    I feel like the keyword there was MORE sanitary.
>    Writing functions that assemble code into a string is definitely
>    not the best way... you lose syntax highlighting, code
>    completion/suggestion, refactoring, etc, in any meaningful way.

AFAIK, the original intent was that string mixins would only rarely be
used... At least, that's the impression I got from reading TDPL.  Sadly,
these days it seems almost every other day somebody else stumbles into a
problem to which string mixins seem to be the default answer.

>    The less your meta requires you resort to strings, the better...
>    and that folds back into my #1 point; storage class separate from
>    the type system is the greatest complexity on meta; almost always
>    leads to text mixins, because there's no machinery for storage
>    classes (or attributes). Can not alias, compound, aggregate...
>    anything.

Truth be told, while I do greatly enjoy D's powerful metaprogramming
features, I also can't help noticing that in some areas it's a little
rough around the edges, and has seemingly-arbitrary asymmetries that
leads to ugliness in generic code. The whole storage class thing is one
example.  As well as historical asymmetries in attributes. Or the
convoluted paraphrases required to do meta coding with attributes. The
functionality is there, but it just needs lots of hoop-jumping,
asymmetric workarounds, and, sadly, string mixins in some cases.

One of the most powerful innovations in D, IMO, is how CTFE allows you
to do compile-time computations *without needing to switch to a special
sub-syntax*.  You just write the code as if it were runtime code, and it
Just Works(tm). This symmetry is what makes it so compelling.  While C++
does offer constexpr, the last time I checked it comes with a number of
syntactic and semantic asymmetries that produce friction and makes it a
little less desirable to work with.

Similarly, compile-time parameters sharing (most of) the runtime
parameter syntax is another winning move that increases symmetry,
reduces friction, and thereby makes it more compelling to work with.

Continuing in the same vein, the syntax required to work directly with
AST entities (as opposed to runtime entities in CTFE) is still a source
of friction. If I want to, say, generate different declarations
depending on what I find in an AliasSeq aka "type tuple", I have to
resort to writing a recursive template instead of just looping over the
sequence.  This is asymmetric, generates friction, and makes it less
pleasant to work with.

In this case, `static foreach` lets you continue using loop syntax, and
is a step in the right direction.  But still, things are still rough
around the edges (e.g., generating unique identifiers to prevent
collision errors, etc.). For example, implementing .map or .joiner over
AliasSeq's would require a lot of ugly paraphrases with recursive
templates, or (shudder) string mixins.

Ideally, compile-time entities should be put on the same level as
runtime entities, so that, for example, you can assign types, storage
classes, function attributes, or indeed AliasSeq's, into compile-time
variables, manipulate them with runtime-like syntax, etc..  This would
remove the unnecessary asymmetries between compile-time / runtime
constructs, reducing friction and thus making it more compelling to work
with.  We may not be able to attain to this ideal given the current
state of things or for historical reasons, but it should be at least a
goal we aim for, rather than introduce more asymmetries to the language.


Fact is stranger than fiction.

Reply via email to