View mode: basic / threaded / horizontal-split · Log in · Help
Chris R. Miller wrote:
> superdan wrote:
>> Andrei Alexandrescu Wrote:
>>
>>> dsimcha wrote:
>>>> == Quote from Andrei Alexandrescu (SeeWebsiteForEmail@erdani.org)'s
>>>> article
>>>>> The problem I see with "!" as a template instantiation is not
>>>>> technical.
>>>>> I write a fair amount of templated code and over years the "!" did not
>>>>> grow on me at all. I was time and again consoled by Walter than one
>>>>> day
>>>>> that will happen, but it never did. I also realized that Walter didn't
>>>>> see a problem with it because he writes only little template code.
>>>>> I didn't have much beef with other oddities unique to D. For
>>>>> example, I
>>>>> found no problem accommodating binary "~" and I was wondering what
>>>>> makes
>>>>> "!" different. I was just looking at a page full of templates and it
>>>>> looked like crap.
>>>>> One morning I woke up with the sudden realization of what the problem
>>>>> was: the shouting.
>>>>> In C, "!" is used as a unary operator. That may seem odd at first, but
>>>>> it nevers follows a word so it's tenuous to associate it with the
>>>>> natural language "!". In D, binary "!" _always_ follows a word, a
>>>>> name,
>>>>> something coming from natural language. So the conotation with
>>>>> exclamation jumps at you.
>>>>> That's why I find the choice of "!" poor. I believe it can impede to
>>>>> some extent acquisition of templates by newcomers, and conversely I
>>>>> believe that using .() can make templates more palatable. I tried
>>>>> using
>>>>> ".()" in my code and in only a couple of days it looked and felt way
>>>>> better to me. Based on that experience, I suggest that "!()" is
>>>>> dropped
>>>>> in favor of ".()" for template instantiation for D2.
>>>>> Sean's argument that "The exclamation mark signifies an assertion of
>>>>> sorts" is exactly where I'd want templates not to be: they should be
>>>>> blended in, not a hiccup from normal code. Serious effort has been,
>>>>> and
>>>>> still is, made in D to avoid shell-shocking people about use of
>>>>> templates, and I think ".()" would be a good step in that direction.
>>>>> Andrei
>>>> Personally, I think that ".()" looks a little too much like a normal
>>>> function/method call.  The "!()" syntax looks just different enough
>>>> to make it
>>>> easy to keep straight in my head that stuff with the "!" is a
>>>> compile-time
>>>> construct and stuff without it can be evaluated at runtime.
>>> I'm arguing we _should_ make template code look like "normal" code,
>>> whatever "normal" is :o). We _should_ strive for "quiet" templates.
>>>
>>> You know what annoys the living heebiejeebies out of me? Nested
>>> template instantiations.
>>>
>>> This!(That!(TheOther!(crap)))
>>>
>>> I have a ton + change of those. In superdan's words: intercourse that.
>>>
>>>
>>> Andrei
>>
>> im a bit drunk. but im much obliged since my name was mentioned n all.
>>
>> ! pisses me off too. i have an opinion. walter looked at unary ops
>> that can be binary ops. first tilde. pulled tat off. ten he wanted the
>> template thing. only 1 left was !. so he put that at work. right walt?
>> now i never like ! because of nother reason. i always forget to put
>> it. and yes it's not needed. i look @ code its unambig. then wtf do i
>> need that crap !
>>
>> anyhoo would be great if ! goes away. to hell with it. need to try
>> asdad.(asdasd) to see how it feels. and yeah they all jump andreis
>> neck whever he posts any. or walts. its funny. hold them guns folks.
>> can i delete this later i wonder.
>
> I live in California.  Legally I cannot own any gun that could
> conceivably hurt someone when misused under the proper situations.  =/
>
> I was never particularly annoyed by the !() syntax, but then again I
> have not written much template code.  I can count the number of template
> classes I have written on one hand.
>
> I am deeply concerned about the proposed .() syntax, however, since (to
> me) it's too similar to a function call.  This lack of finding some kind
> of counterproposal left me studying my keyboard for any unused
> characters that could become a symbol.  I saw these symbols that I could
> remember no existing use for:
>
> @, #, \$,
>
> I'm favorable to the # symbol.  So a template would look like
> foo#(int)(bar).
>
> To me the # symbol looks like it could be two Latin 't' characters (not
> to be confused with the Greek Tau 'T', which is different).  Thus one
> could remember the mnemonic "template" with two t's for the # symbol.
>
> Just a countersuggestion, I haven't really looked through specifications
> or anything to verify the unused status of any of those symbols.

No go due to #line. :o(

Andrei

On Mon, 06 Oct 2008 04:03:08 +0400, Andrei Alexandrescu
<SeeWebsiteForEmail@erdani.org> wrote:

> Jason House wrote:
>> Andrei Alexandrescu wrote:
>>
>>> Leandro Lucarella wrote:
>>>> "enum" as the way to declare manifest constants were much more ugly
>>>> than
>>>> "!(", and most of the people were against it. I don't see why ".("
>>>> should
>>>> be introduced.
>>> Ugly or not, enumerated symbols were manifest constants to begin with.
>>> That's kinda hard to argue against because it's a sheer fact.
>>  Even before we expanded enums, I hated how there was no toString() for
>> enum
>> types.
>
> defineEnum in std.typecons provides that (and parsing too).
>
>> Walter explained this was because bit masks made it difficult. I
>> like using enums for a restricted set of options.  That'd allow the
>> compiler to provide toString and catch misuse in switch statements...
>> Including when I add or remove allowed values.
>
> Walter hasn't gotten around to implementing the final switch statement,
> which does exactly as you mention. For the interested I paste the
> relevant section in TDPL - hot off the oven:
>
> \section{The \protect\cc{final switch} Statement}
>
> It is  often the case  that @switch@ is  meant to handle  all possible
> cases,  such as  all values  of a  small integer  or of  an enumerated
> type. If, during maintenance, the  number of cases is changing, all of
> the dependent @switch@  statements suddenly fall out of  sync and must
> be  manually searched  for  and modified.   For  such situations,  the
> \cc{final switch} statement comes in handy:
>
> \begin{D}
> enum deviceStatusMask = 3;
> ...
> void Handle(uint x) {
>    final switch (x & deviceStatusMask) {
>    case 0: ...
>    case 1: ...
>    case 2: ...
>    case 3: ...
>    }
> }
> \end{D}
>
> Should  the value of  @mask@ change  later to,  say, 7,  attempting to
> recompile @Handle@ is met with refusal on the following grounds:
>
> \begin{lstlisting}[language=sys]
> Error: final switch statement must handle all values
> \end{lstlisting}
>
> The \cc{final switch} statement looks  at the shape of its controlling
> expression to figure out the bounds, as follows:
>
> \begin{itemize*}
> \item  If \meta{expression}  is \metai{e}  @&@ \metaii{e}  and  one of
>    \metai{e} and \metaii{e} evaluates  to a positive compile-time value
>    @c@, then the range is determined as @0@ up to (and including) @c@.
> \item  If  \meta{expression}   is  \metai{e}  \cc{\%}  \metaii{e}  and
>    \metaii{e} evaluates to a  positive compile-time value @c@, then the
>    range is determined as @0@ up to (and not including) \cc{c}.
> \item  If  \meta{expression}  is   an  unsigned  right  shift  (either
>    \metai{e}  \cc{>>}  \metaii{e}  operating  on unsigned  numbers,  or
>    \metai{e}  \cc{>>>} \metaii{e}),  and if  \metaii{e} evaluates  to a
>    compile-time value  @c@, then the range  is determined as  @0@ up to
>    (and not including) \cc{1 << (8 *} \metai{e}\cc{.sizeof - c)}.%>>
> \item If  \meta{expression} is  the assignment variant  of one  of the
>    above  (@&=@, \cc{\%=}  etc.) then  the range  the same  as  for the
>    non-assignment variant.
> \item If  \meta{expression} is  an enumerated type,  the range  is the
>    entire set of values of the enumerated type.
> \item   Otherwise,  the  range   is  @e.min@   up  to   and  including
>    @e.max@. (The @min@ and @max@  constants are defined for all numeric
>    types.)
> \end{itemize*}
>
> There are quite a few other  cases in which the range of an expression
> could be  effectively determined,  but \cc{final switch}  only handles
> the  usual ones.   A @default@  label  is allowed  inside a  \cc{final
>    switch} statement  and practically turns  off all checks  because it
> ensures \emph{de facto} that all values are handled.
>
>
> Andrei

Final switch is great, but why allow default case in it? Putting the
default case into the final switch effectively transforms it into a
regular one discarding all of its benefits. What's the point?

Denis Koroskin wrote:
> On Mon, 06 Oct 2008 04:03:08 +0400, Andrei Alexandrescu
> <SeeWebsiteForEmail@erdani.org> wrote:
>
>> Jason House wrote:
>>> Andrei Alexandrescu wrote:
>>>
>>>> Leandro Lucarella wrote:
>>>>> "enum" as the way to declare manifest constants were much more ugly
>>>>> than
>>>>> "!(", and most of the people were against it. I don't see why ".("
>>>>> should
>>>>> be introduced.
>>>> Ugly or not, enumerated symbols were manifest constants to begin with.
>>>> That's kinda hard to argue against because it's a sheer fact.
>>>  Even before we expanded enums, I hated how there was no toString()
>>> for enum
>>> types.
>>
>> defineEnum in std.typecons provides that (and parsing too).
>>
>>> Walter explained this was because bit masks made it difficult. I
>>> like using enums for a restricted set of options.  That'd allow the
>>> compiler to provide toString and catch misuse in switch statements...
>>> Including when I add or remove allowed values.
>>
>> Walter hasn't gotten around to implementing the final switch
>> statement, which does exactly as you mention. For the interested I
>> paste the relevant section in TDPL - hot off the oven:
>>
>> \section{The \protect\cc{final switch} Statement}
>>
>> It is  often the case  that @switch@ is  meant to handle  all possible
>> cases,  such as  all values  of a  small integer  or of  an enumerated
>> type. If, during maintenance, the  number of cases is changing, all of
>> the dependent @switch@  statements suddenly fall out of  sync and must
>> be  manually searched  for  and modified.   For  such situations,  the
>> \cc{final switch} statement comes in handy:
>>
>> \begin{D}
>> enum deviceStatusMask = 3;
>> ...
>> void Handle(uint x) {
>>    final switch (x & deviceStatusMask) {
>>    case 0: ...
>>    case 1: ...
>>    case 2: ...
>>    case 3: ...
>>    }
>> }
>> \end{D}
>>
>> Should  the value of  @mask@ change  later to,  say, 7,  attempting to
>> recompile @Handle@ is met with refusal on the following grounds:
>>
>> \begin{lstlisting}[language=sys]
>> Error: final switch statement must handle all values
>> \end{lstlisting}
>>
>> The \cc{final switch} statement looks  at the shape of its controlling
>> expression to figure out the bounds, as follows:
>>
>> \begin{itemize*}
>> \item  If \meta{expression}  is \metai{e}  @&@ \metaii{e}  and  one of
>>    \metai{e} and \metaii{e} evaluates  to a positive compile-time value
>>    @c@, then the range is determined as @0@ up to (and including) @c@.
>> \item  If  \meta{expression}   is  \metai{e}  \cc{\%}  \metaii{e}  and
>>    \metaii{e} evaluates to a  positive compile-time value @c@, then the
>>    range is determined as @0@ up to (and not including) \cc{c}.
>> \item  If  \meta{expression}  is   an  unsigned  right  shift  (either
>>    \metai{e}  \cc{>>}  \metaii{e}  operating  on unsigned  numbers,  or
>>    \metai{e}  \cc{>>>} \metaii{e}),  and if  \metaii{e} evaluates  to a
>>    compile-time value  @c@, then the range  is determined as  @0@ up to
>>    (and not including) \cc{1 << (8 *} \metai{e}\cc{.sizeof - c)}.%>>
>> \item If  \meta{expression} is  the assignment variant  of one  of the
>>    above  (@&=@, \cc{\%=}  etc.) then  the range  the same  as  for the
>>    non-assignment variant.
>> \item If  \meta{expression} is  an enumerated type,  the range  is the
>>    entire set of values of the enumerated type.
>> \item   Otherwise,  the  range   is  @e.min@   up  to   and  including
>>    @e.max@. (The @min@ and @max@  constants are defined for all numeric
>>    types.)
>> \end{itemize*}
>>
>> There are quite a few other  cases in which the range of an expression
>> could be  effectively determined,  but \cc{final switch}  only handles
>> the  usual ones.   A @default@  label  is allowed  inside a  \cc{final
>>    switch} statement  and practically turns  off all checks  because it
>> ensures \emph{de facto} that all values are handled.
>>
>>
>> Andrei
>
> Final switch is great, but why allow default case in it? Putting the
> default case into the final switch effectively transforms it into a
> regular one discarding all of its benefits. What's the point?

Good point. I was thinking of making maintenance easier: sometimes you
decide to insert a default. Then you'd need to wipe the "final" too.
Then you comment out the default. You'd need to add the "final" back
(and you may forget). And so on.

Andrei

On Sun, Oct 5, 2008 at 8:13 PM, Denis Koroskin <2korden@gmail.com> wrote:

> Final switch is great, but why allow default case in it? Putting the default
> case into the final switch effectively transforms it into a regular one
> discarding all of its benefits. What's the point?
>

I was going to ask the same thing.

Ary Borenszweig wrote:
> Andrei Alexandrescu escribió:
>> The problem I see with "!" as a template instantiation is not
>> technical. I write a fair amount of templated code and over years the
>> "!" did not grow on me at all. I was time and again consoled by Walter
>> than one day that will happen, but it never did. I also realized that
>> Walter didn't see a problem with it because he writes only little
>> template code.
>>
>> I didn't have much beef with other oddities unique to D. For example,
>> I found no problem accommodating binary "~" and I was wondering what
>> makes "!" different. I was just looking at a page full of templates
>> and it looked like crap.
>>
>> One morning I woke up with the sudden realization of what the problem
>> was: the shouting.
>>
>> In C, "!" is used as a unary operator. That may seem odd at first, but
>> it nevers follows a word so it's tenuous to associate it with the
>> natural language "!". In D, binary "!" _always_ follows a word, a
>> name, something coming from natural language. So the conotation with
>> exclamation jumps at you.
>
> I was thinking about in which other way templates could be specified...
> Most of the other symbols already have a meaning as binary operator. And
> it also would be nice to have an opening and closing symbols, like <> in
> Java, C++, etc.

I remember reading that Walter specifically rejected that idea because
it became ambiguous with bitshift operations.  In my time with Java, I
saw many of these:

public class Foo<Bar<o extends ArrayList>> { ... }

When instantiated in code the >> at the end of the template declaration
does become very ambiguous with a bitshift.

> Can't {} be used for that? For example:
>
> List{int} someList;
>
> void foo{T}(T val) {
> }
>
> It seems more quiet. :-)

I rejected that idea when I was searching for counterproposals because
it conflicts with the code-block syntax.  The existing () is much less
ambiguous, since when you encounter two sets of parenthesis it must be a
template.  If it were multiplication it would require the explicit *
binary operator.

The !() syntax seems to serve only as a heads up that it's a template.
Otherwise (as far as I can tell) a simple foo(int)(bar, baaz) would work
just as well as foo!(int)(bar, baaz).

Andrei Alexandrescu wrote:
> Sean Kelly wrote:
>> Andrei Alexandrescu wrote:
>>> I believe the clear distinction is not only unnecessary, but
>>> undesirable. We should actively fight against it.
>>
>> Why is it undesirable?  I like the idea of static parameters as
>> described at the conference last year, but I don't think that has any
>> bearing on this particular issue.
>
> Time and again, both Walter and myself have met people who experience a
> mental block whenever templates come within a mile. The fact that they
> come with the sign Here! This! Is! A! Template! doesn't help any.

Are we discussing better template syntax of a magical injection that
will cure the common fear of template code?  ;-)

Andrei Alexandrescu wrote:
> KennyTM~ wrote:
>> Andrei Alexandrescu wrote:
>>> Michel Fortin wrote:
>>>> On 2008-10-05 01:14:17 -0400, Andrei Alexandrescu
>>>> <SeeWebsiteForEmail@erdani.org> said:
>>>>
>>>> -- snip --
>>>>
>>>> Or we could use special delimiter characters:
>>>>
>>>>     Positive<real>(joke);
>>>>     Positive“real”(joke);
>>>>     Positive«real»(joke);
>>>>     Positive#real@(joke);
>>>>
>>>> Each having its own problem though.
>>>>
>>>> My preference still goes to "!(".
>>>
>>> There was also Positive{real}(joke), with which I couldn't find an
>>> ambiguity.
>>  >
>>
>> Ohhhh. Why isn't it considered then? (Suppose we already knew Positive
>> is not a keyword and not preceded by the keywords struct, class, etc.)
>
> I believe it should be considered. At some point there was discussion on
> accepting a last parameter of delegate type outside the function parens.
> The idea was to allow user-defined code to define constructs similar to
> e.g. if and foreach.
>
> But I think that has many other problems (one of which is that the
> delegate can't reasonably specify parameters), so we can safely discount
> that as a problem.
>
> I'd want to give it a try. How do others feel about Template{arguments}?

I distrust it greatly.  Too similar to a code block.

I still have a lot of contact with kids still being educated in
programming, and that'd probably just confuse the heck out of 'em.

No on {}!  Keep the code block sacred!  ;-)

On Sun, Oct 5, 2008 at 8:57 PM, Chris R. Miller
<lordsauronthegreat@gmail.com> wrote:
>
> The !() syntax seems to serve only as a heads up that it's a template.
> Otherwise (as far as I can tell) a simple foo(int)(bar, baaz) would work
> just as well as foo!(int)(bar, baaz).
>

Unambiguous grammar, you fail it.

foo(bar)(baz); // template instantiation or a chained call?

This _can_ be _made_ to work, but it would mean that the parse tree
would be dependent upon semantic analysis, and that just makes things
slow and awful.  I.e. C++.

On Mon, 06 Oct 2008 00:55:33 +0100, Andrei Alexandrescu
<SeeWebsiteForEmail@erdani.org> wrote:
>>>
>>> Andrei
>>  I disagree. I'm not saying its easy but it could be done. We would
>> have to start
>> with something relatively simple and work our way up but it could be
>> done.
>
> I, too, think it can be done in the same way supersonic mass
> transportation can be done, but having done research in the area I can
> tell you you are grossly underestimating the difficulties. It is a
> project of gargantuan size. Today the most advanced systems only managed
> to automatically prove facts that look rather trivial to the casual
> reader. There is absolutely no hope for D to embark on this.
>
I'm not asking for a generalised theorem prover. Something to handle even
the
simple cases is a start.
I agree that D won't have this (any time soon) but mainly because there
are several hundred things higher up the priority list.

>> Compiler's already do all kinds of clever analyses behind the scenes
>> but each one
>> is often hard coded. I suspect the main difficulty is giving users too
>> much rope
>> by which to hang themselves, or rather hang the compiler trying to
>> prove something
>> it doesn't realise it can't. Marrying declarative / constraint based
>> programming
>> at compile time is creeping in via templates. I wish it was less well
>> hidden.
>
> I discussed the problem this morning with Walter and he also started
> rather cocky: if you assert something early on, you can from then on
> assume the assertion is true (assuming no assignment took place, which
> is not hard if you have CFA in place). He got an arrow in a molar with
> the following example:
>
> double[] vec;
> foreach (e; vec) assert(e >= 0);
> // now we know vec is all nonnegatives
> normalize(vec);
>
> The definition of normalize is:
>
> void normalize(double[] vec)
> {
>      foreach (e; vec) assert(e >= 0);
>      auto sum = reduce@"a + b"(vec, 0);
>      assert(sum > 0);
>      foreach (ref e; vec) e /= sum;
> }
>
> If normalize takes udouble[] and you have one of those, there's no need
> to recheck. Automated elimination of the checking loop above is really
> hard.
>
>
> Andrei

Is it? I think your example needs to be expanded or we may be talking
at cross purposes.
Firstly I would rearrange things a little, though in principle it makes
no difference.

pre
{
static assert(foreach (e; vec) assert(e >= 0));
}
void normalize(double[] vec)
{
auto sum = reduce@"a + b"(vec, 0);
assert(sum > 0);
foreach (ref e; vec) e /= sum;
}

double[] vec;
static assert(foreach (e; vec) assert(e >= 0));  // line X

// now we know vec is all nonnegatives
normalize(vec);   // line Y

Imagine I have a prolog style symbolic unification engine to hand inside
my compiler.

At line X the static assertion is evaluated.
The logical property e>=0 is asserted on the vec symbol.
The compiler reaches line Y.
Vec has not been modified so still has this property associated with it.
We now unify the symbol representing vec with the contract on normalise.
The unification succeeds and everything is fine.

Now imagine we have a stupid analyser.

double[] vec;
static assert(foreach (e; vec) assert(e >= 0));  // line X

vec[0] -= 1; // line Z

// now we know vec is all nonnegatives
normalize(vec);   // line Y

when the compile time analyser reaches line Z it can't work out whether or
not the
contract still applies, so it removes the assertion that vec is e>=0 for
all elements, or
rather asserts that it is not provably true.
Now when we reach Y the unification fails.
We don't throw a compile time contraint violation error. We haven't proved
it to have failed.
We throw a compile time constraint unprovable error or warning.

Its like a lint warning. Your code may not be wrong but you might want to
consider altering it
in a way that makes it provably correct.

We would really like to be able to assert that certain properties always
hold for a variable
through its life-time. That is a harder problem.

I see something like this as a basis on which more advanced analysis can

Actually re-using static assert above was probably misleading. It would be
better to have something
that says talk to the compile time theorem prover (a prolog interpreter
would do).

I have been meaning to write something along these lines for years but so
far I haven't got around to it
so I might as well stop keeping it under my hat.

Regards,

Bruce.

On 2008-10-05 20:57:31 -0400, "Chris R. Miller"
<lordsauronthegreat@gmail.com> said:

> The !() syntax seems to serve only as a heads up that it's a template.
> Otherwise (as far as I can tell) a simple foo(int)(bar, baaz) would
> work just as well as foo!(int)(bar, baaz).

Well, not so sure about that: I'm pretty sure it's needed for
disambiguation too. Let's say you have:

void foo(int x)();
void foo(T)(T x);

foo(5);

Is foo(5) a the same as foo!(5), or does it call foo!(int).foo(5) ?
Under the current rules, it's the second (you can write foo!(5) to call
the first). If you allow templates to be instanciated without the "!",
then I guess both will match and you'll have ambiguity.

If you could avoid having sets of parameters, one for the function and
one for the template, then you could get rid of the "!" in a snap...

Well, maybe not. You'll still have to resolve the same issues for constructors:

class A(int x) {
this() {}
}
class A() {
this(int x) {}
}

auto a = new A(5);

Perhaps this is not a problem however: the only two valid ways to
create an instance of one or the other are "new A!(5)" or "new
A!()(5)", which would translate in non-"!" syntax as: "new A(5)" and
"new A()(5)". Unfortunately, we don't have this "luck" with functions.

--
Michel Fortin
michel.fortin@michelf.com
http://michelf.com/
`
11 12 13 14 15 16 17 18 19