On Oct 26, 2020, at 2:10 PM, Ville Voutilainen <ville.voutilainen@gmail.com> wrote:

On Mon, 26 Oct 2020 at 19:58, Jeremy Ong via SG7 <sg7@lists.isocpp.org> wrote:

 Quick tangent:

I’ve come to the conclusion that the syntax of certain metaprogramming features should be sufficiently different from existing C++ notation, perhaps even rising to the level of “unfamiliar”.

I'm curious if this is the position others on the committee are leaning towards. Personally, I'm not sure I agree with this sentiment (although many other aspects of the proposed seem like great features and additions to the core language). In particular, we currently have at least 2 paradigms for authoring compile-time code: templates and constexpr. These techniques are often at odds with each other and traversing the type boundary can be somewhat awkward. Much of the advancement in C++17/20 has served to blur the lines so that compile-time programming is accessible to programmers without needing to resort to traditional template techniques, many of which are decidedly cumbersome compared to their imperative alternatives (fold expressions as an example, are a common source of confusion, especially among beginners).

While C++ has long been known as a "federated language," where practitioners are expected to learn 4 (?) paradigms of coding, should we not tread carefully before creating a fifth, or potentially creating hybrid programming models among those existing? Personally, it's hard not to find compilers such as Zig (ref. comptime) and Circle compelling. The programming model is uniform, and compile-time code and runtime-code are easily shared (in the current status-quo, many algorithms are implemented twice, once in runtime-land, once in compile-time land).

Summarizing, it's unclear to me that the benefits cited for composing an "unfamiliar" syntax is worth it. Blurring the lines between compile time and runtime code isn't intrinsically "bad" (in my opinion). In fact, this is potentially a valuable feature to make code fungible in both compile and runtime contexts.

The part you quoted says "of certain metaprogramming facilities". They
stand out, which is kinda nice when
you do normal compile-time computations interspersed with injections.
If the meta-bits blend in, it's a bit harder
to see where the different worlds are. Those different worlds are not
clashing, they blend in controlled ways.
The different-world-mixture in p2237 doesn't look alarming to me, but
then again, my experience in such
mixtures comes from Common Lisp, not Circle.

One alternative way to make meta content stand out: always express it via strings literals.

I’m not trying to be cute: we could still do fragments semantically, while using string injection syntax, and thereby perhaps get the best of both worlds.  E.g.:

consteval void create_var_42(const char *name) {
  inject("int " + name + " = 42;");
The compiler could create a fragment with content `int |# name #| = 42;` from that syntax.  I.e. the implementation could tentatively parse the non dependent string literals and do as much semantic verification as possible from them.  (The user could also give guarantees about the dependent string literals to help the compiler out: e.g. mark that parameter `(const char *name [[nospaces]])`.) 

This would let the compiler implementation sort through the confusion & matters of hygiene, not the user.