August 21, 2006
> The problem with requiring the { } around the argument is that programmers just don't like it. I don't think I can make them like it.

I am a programmer. I like them. I like to understand my and foreign code. I hate C++ for that reason. All the time i have to look in hundred files and have thousands of informations to care about only to get a clue of what is going on.

Implicit or explicit, this is really an important step.
A decision between readability in big projects and nice looking example
code.

http://www.digitalmars.com/d/overview.html
 Who D is For
 Teams who write apps with a million lines of code in it.

Look at the example with explicit delegates:

void foo()
{
    int v = 2;
    cond
    ({
	scase(v == 1, {writefln("it is 1")}),
	scase(v == 2, {writefln("it is 2")}),
	scase(v == 3, {writefln("it is 3")}),
	scase(true,   {writefln("it is the default")})
    });
}

Why should one not like it? It looks sexy ;D



August 21, 2006
Frank Benoit wrote:
> I think the lazy eval is a great feature, but in this form it has also
> great drawbacks.
> 
> The code isn't that much readable as it was before. You don't know what
> will happen. Will that expression be evaluated or not? Or will it be
> evaluated more than once?
> 
> There is no possibility to choose between
> 
> func( char[] a ) vs. func( char[] delegate() dg )
> 
> func( funcRetInt() ); vs. func( &funcRetInt );
> 
> It would be really important for me to have readable code. I want to
> look at the code, and want to have an impression what will happen.
> 
> What really would help, is if the the delegate is marked in some way as
> such. In the last releases of DMD the {} syntax was there. With it you
> needed the return statement. Perhaps we can choose the {} syntax with an
> optional return statement....
> 
> 
> { "abc"         } => char[] delegate()
> { return "abc"; } => char[] delegate()
> 
> func( "abc" ) calls func( char[] a )
> func({ "abc" }) calls func( char[] delegate() dg )
> func({ return "abc"; }) calls func( char[] delegate() dg )
> 
> With that syntax one can immidiatly see "this is a delegate, if it is
> called or not or more than once depends on func()" and the more typing
> of {} is not too much.


So, we have some existing code for logging. Very efficient, and highly flexible. It follows the design patterns popularized by Log4J, with Appenders, Layouts, etc

The D version of a logger has five 'levels' of logging:

log.trace (char[])
log.info (char[])
log.warn (char[])
log.error (char[])
log.fatal (char[])

and the intent (with dmd.164) was to add these flavours, specifically to address the point Walter makes about unnecessary message construction:

log.trace (char[] delegate() dg)
log.info (char[] delegate() dg)
log.warn (char[] delegate() dg)
log.error (char[] delegate() dg)
log.fatal (char[] delegate() dg)

The support code was installed fairly recently, and the above delegate-wrappers were on the todo list.

However, dmd.165 will presumeably not compile this code? Won't there be an overload ambiguity? If so, it illustrates a serious shortcoming in the new syntax
August 22, 2006
On Mon, 21 Aug 2006 15:16:34 -0700, kris wrote:

> Derek Parnell wrote:
>> On Mon, 21 Aug 2006 14:18:04 -0700, Walter Bright wrote:
>> 
>>>Frank Benoit wrote:
>>>
>>>>I think the lazy eval is a great feature, but in this form it has also great drawbacks.
>>>>
>>>>The code isn't that much readable as it was before. You don't know what will happen. Will that expression be evaluated or not? Or will it be evaluated more than once?
>>>
>>>It's true there is no clue from the user's side which it is. But there also isn't a clue whether the arguments are in, out, or inout. There also is no syntactic clue what the function *does*. One must look at the function interface and documentation to use it successfully anyway.
>>>
>>>It's going to take some caution to use this capability in a productive way.
>>>
>>>
>>>>There is no possibility to choose between
>>>>
>>>>func( char[] a ) vs. func( char[] delegate() dg )
>> 
>> Would it possible to use ...
>> 
>>    func ( cast(char[]) "abc" );
>> 
>> to force the compiler to chose 'func( char[] a)' instead of the delgated
>> version?
>> 
> 
> arghhh!!! Please ... cast() is only for exceptional circumstances :(

And this situation is not exceptional? Okay, than how about a keyword ...

  func ( forget_the_delegate_and_use_the_other_type_instead "abc" );

;-)


-- 
Derek
(skype: derek.j.parnell)
Melbourne, Australia
"Down with mediocrity!"
22/08/2006 10:43:28 AM
August 22, 2006
On Mon, 21 Aug 2006 16:33:00 -0700, Walter Bright wrote:

> Frank Benoit wrote:
>> For example i have a container with a add( element ) method. Now this
>> method is implemented with lazy-eval, if the container is already full,
>> the argument is not evaluated.
>> Or the get( index ) method does not eval the arg if the container is
>> empty. And every time I have not only to remember the method name and
>> arguments, now I have to know details of the implementation.
>> Great, that was the silly thing they wanted to take away from me with
>> this OOP ;)
>> 
>> Solution: Force the user the make {} around a delegate argument.
>> 
>> Now I get a compiler error when writing container.get(i++); Then I know,
>> this is a delegate, take care with the increment, writing
>> container.get({i}); i++;
> 
> While I understand your concern, I don't think the examples illustrate it. If a get() is done for an element that doesn't exist, the usual way to deal with it is throw an exception, and have some complex scheme to try and undo the side effects from evaluating the argument. With lazy evaluation, no need to undo the side effects, as only if the get() is guaranteed to succeed will the argument be evaluated.
> 
> The problem with requiring the { } around the argument is that programmers just don't like it. I don't think I can make them like it.

Huh? You asked them all? You didn't ask me and I like it.
-- 
Derek
(skype: derek.j.parnell)
Melbourne, Australia
"Down with mediocrity!"
22/08/2006 10:50:15 AM
August 22, 2006
Derek Parnell wrote:
> On Mon, 21 Aug 2006 16:33:00 -0700, Walter Bright wrote:
>> The problem with requiring the { } around the argument is that programmers just don't like it. I don't think I can make them like it.
> 
> Huh? You asked them all? You didn't ask me and I like it. 

Did you use it before 0.165? Did anyone? Everyone I'd show the { } syntax to reacted with zzzzzz. I show them the one without, and all of a sudden they are excited about it and can think of all kinds of uses.

C++ programmers have been trying to do this for over a decade - first with preprocessor macros, and now with expression templates.

Perhaps what is so off-putting of this feature, as opposed to out, inout, const, implicit conversions, and other effects that require one to look at the interface, is it's very unusual (even unique?) for a C-like language.
August 22, 2006
kris wrote:
> So, we have some existing code for logging. Very efficient, and highly flexible. It follows the design patterns popularized by Log4J, with Appenders, Layouts, etc
> 
> The D version of a logger has five 'levels' of logging:
> 
> log.trace (char[])
> log.info (char[])
> log.warn (char[])
> log.error (char[])
> log.fatal (char[])
> 
> and the intent (with dmd.164) was to add these flavours, specifically to address the point Walter makes about unnecessary message construction:
> 
> log.trace (char[] delegate() dg)
> log.info (char[] delegate() dg)
> log.warn (char[] delegate() dg)
> log.error (char[] delegate() dg)
> log.fatal (char[] delegate() dg)
> 
> The support code was installed fairly recently, and the above delegate-wrappers were on the todo list.
> 
> However, dmd.165 will presumeably not compile this code?

Correct.

> Won't there be an overload ambiguity?

Yes.

> If so, it illustrates a serious shortcoming in the new syntax

Just get rid of the (char[]) versions. You could argue "what about the efficiency?"

1) Passing a delegate is exactly the same number of instructions as passing a char[], i.e., it is two values being passed.

2) Actually calling the dg() will, of course, cost more instructions than just referencing a []. This is mitigated by, presumably, logging being normally off, and being overshadowed by the rest of the actual logging cost.

3) It is possible that the delegate can be inlined, thus eliminating any extra overhead.
August 22, 2006
On Mon, 21 Aug 2006 19:41:24 -0700, Walter Bright wrote:

> Derek Parnell wrote:
>> On Mon, 21 Aug 2006 16:33:00 -0700, Walter Bright wrote:
>>> The problem with requiring the { } around the argument is that programmers just don't like it. I don't think I can make them like it.
>> 
>> Huh? You asked them all? You didn't ask me and I like it.
> 
> Did you use it before 0.165?

Yes. However I couldn't release it in Build until GDC caught up to the ability, so I took it out again.

> Did anyone? Everyone I'd show the { } syntax to reacted with zzzzzz.

I didn't.

> I show them the one without, and all of a sudden they are excited about it and can think of all kinds of uses.

And so am I, but now I *also* wary of the pitfalls that this implicit
conversion to delegates open up.

> C++ programmers have been trying to do this for over a decade - first with preprocessor macros, and now with expression templates.

And do I give a toss about C++ programming foibles ;-)

> Perhaps what is so off-putting of this feature, as opposed to out, inout, const, implicit conversions, and other effects that require one to look at the interface, is it's very unusual (even unique?) for a C-like language.

Not sure about that. I see the problem with existing calls to functions suddenly behaving differently with no apparent reason. With this new ability, a library writer can change the interface and my program will still compile, but may now behave in unexpected ways.


Let's assume the original API said

   void SomeFunc(char[] x);

so I code ...

 void xyzzy(inout x)
 {
    char[] r = std.string.format("%d", x);
    x++;
 }
 . . .
 int y = 7;
 SomeFunc( std.string.format("The number is " ~ xyzzy(y)) );


Then the library writer changes this to

   void SomeFunc(char[] delegate() x);

My code still compiles but there is now no guarantee that my xyzzy() function will be called. The API function may chose to not call it for some reason valid to itself. But I'm left scratching my head trying to work out why my variable 'y' is sometimes being updated and other times not.


-- 
Derek
(skype: derek.j.parnell)
Melbourne, Australia
"Down with mediocrity!"
22/08/2006 12:49:40 PM
August 22, 2006
On Mon, 21 Aug 2006 19:41:24 -0700, Walter Bright <newshound@digitalmars.com> wrote:

> Derek Parnell wrote:
>> On Mon, 21 Aug 2006 16:33:00 -0700, Walter Bright wrote:
>>> The problem with requiring the { } around the argument is that programmers just don't like it. I don't think I can make them like it.
>>  Huh? You asked them all? You didn't ask me and I like it.
>
> Did you use it before 0.165? Did anyone? Everyone I'd show the { } syntax to reacted with zzzzzz. I show them the one without, and all of a sudden they are excited about it and can think of all kinds of uses.
>


Zzzz?  I really don't think that was the response.  I believe most people here were quite excited about the { } form when it came out (especially after experimenting with it for a time).  They just may not have expressed it in your hearing (ie. in this newsgroup where you are so accustomed to feedback).  Drop in the #D IRC channel on freenode sometime.  You might see more discussion about the language then you imagine.  I've run across many people that were overjoyed with {} feature, including long time D users here.

Silence doesn't mean that there's no support for a feature.  In the context of D, silence really is a good sign.  As you know, most people here are quite outspoken about any little feature added that might not be consistant with the D language. :)

-JJR


August 22, 2006
Walter Bright wrote:
> Derek Parnell wrote:
> 
>> On Mon, 21 Aug 2006 16:33:00 -0700, Walter Bright wrote:
>>
>>> The problem with requiring the { } around the argument is that programmers just don't like it. I don't think I can make them like it.
>>
>>
>> Huh? You asked them all? You didn't ask me and I like it. 
> 
> 
> Did you use it before 0.165? Did anyone? Everyone I'd show the { } syntax to reacted with zzzzzz. I show them the one without, and all of a sudden they are excited about it and can think of all kinds of uses.


I did, and continue to do so, along with a number of others I know working on delegate-oriented systems.

What was unweidly about the prior syntax was simply the return and the 'superfluous' semicolons. The braces don't get in the way at all, and in fact, make it quite clear exactly what is going on.

As it stands now, blatant ambiguities have been introduced. And for what?

> 
> C++ programmers have been trying to do this for over a decade - first with preprocessor macros, and now with expression templates.

Eh. Callbacks have been around for decades. Syntax to support that is good or bad depending upon language you choose.

> 
> Perhaps what is so off-putting of this feature, as opposed to out, inout, const, implicit conversions, and other effects that require one to look at the interface, is it's very unusual (even unique?) for a C-like language.

Perhaps, but that doesn't mean the syntax needs to to be ambiguous. Does it? We all know that perfectly well.


August 22, 2006
Derek Parnell wrote:
> On Mon, 21 Aug 2006 19:41:24 -0700, Walter Bright wrote:
> 
>> Derek Parnell wrote:
>>> On Mon, 21 Aug 2006 16:33:00 -0700, Walter Bright wrote:
>>>> The problem with requiring the { } around the argument is that programmers just don't like it. I don't think I can make them like it.
>>> Huh? You asked them all? You didn't ask me and I like it. 
>> Did you use it before 0.165? 
> 
> Yes. However I couldn't release it in Build until GDC caught up to the
> ability, so I took it out again.
> 
>> Did anyone? Everyone I'd show the { } syntax to reacted with zzzzzz. 
> 
> I didn't.

Ok.

>> I show them the one without, and all of a sudden they are excited about it and can think of all kinds of uses.
> And so am I, but now I *also* wary of the pitfalls that this implicit
> conversion to delegates open up.

It's good to be wary of unusual new features. There are usually unanticipated problems with them.


>> C++ programmers have been trying to do this for over a decade - first with preprocessor macros, and now with expression templates.
> And do I give a toss about C++ programming foibles ;-)

LOL! But expression templates, horrible hack that they are, are often listed as the reason C++ is so powerful and therefore why should one change? Expression templates are how C++ does "domain specific languages" and David Abraham explains them to packed conference rooms. So I believe there is a serious demand for them, but not the way C++ does it, as probably only 5 people on the planet are able to create a DNS using them.

With the lazy evaluation thing, though, writing DNSs becomes simple and straightforward, which may (just may) catapult D forward like defmac rescued Lisp from oblivion.


> Not sure about that. I see the problem with existing calls to functions
> suddenly behaving differently with no apparent reason. With this new
> ability, a library writer can change the interface and my program will
> still compile, but may now behave in unexpected ways.

He can do that anyway - I've already enumerated several ways. But there's been an awful lot of threads here that boil down to adding more information to the function declaration so that it's easier for the user to see what is happening. (And it's a little hard to hide a delegate declaration!)

I should also point out that:
	void foo(int x);
and:
	void foo(int delegate() x);
will have different name mangling. So if you change the library, and don't recompile the user code, it won't link.


> Let's assume the original API said 
> 
>    void SomeFunc(char[] x);
> 
> so I code ...
> 
>  void xyzzy(inout x)
>  {
>     char[] r = std.string.format("%d", x);
>     x++;
>  }
>  . . .
>  int y = 7;
>  SomeFunc( std.string.format("The number is " ~ xyzzy(y)) );
> 
> 
> Then the library writer changes this to 
> 
>    void SomeFunc(char[] delegate() x);
> 
> My code still compiles but there is now no guarantee that my xyzzy()
> function will be called. The API function may chose to not call it for some
> reason valid to itself. But I'm left scratching my head trying to work out
> why my variable 'y' is sometimes being updated and other times not.

Suppose there's an API function:

	void anotherFunc(int x);

which I call:

	int y = 3;
	anotherFunc(y);
	writefln(y);

then the library writer changes the API to:

	void anotherFunc(inout int x);

and internally increments x. I'm left wondering why 4 is now being printed instead of 3.

Changing an API and thereby breaking (obviously or subtly) the user code is as old as programming ("DLL hell" is a term probably older than many programmers!). The only answer I can think of is, if you must change the API of a function, and you have legacy users of it, give the changed one a new name and tag the old name with 'deprecated'.