August 23, 2006
Sean Kelly wrote:
> Walter Bright wrote:
> 
>> Sean Kelly wrote:
>>
>>> I'll admit that doing so makes a difference to me, as it allows for tricks that aren't possible with explicit delegate signifiers.  But I'm undecided as to whether this is a "good thing" or not--I plan to give it some time before I decide.
>>
>>
>> Good - I don't think any of us are prescient enough to see all the ramifications of this. Nobody predicted where C++ templates wound up (the good and the bad).
> 
> 
> C++ templates are well-intentioned and really quite powerful, but they're a horrible hack, even when used as originally intended.  The need for "template" as a means to distinguish dependent type names is a perfect example of something that should be completely unnecessary.
> 
>>> And I'd like to note that all the tricks I'm thinking of for the new syntax tend to involve refactoring existing code (which is potentially dangerous, given the issue with side-effects) rather than writing new code.  With new code I don't see much benefit to omitting all evidence that a parameter will be converted to a delegate, as it's something the user must be aware of to write correct code.
> 
> 
> In light of my opening paragraph, I really shoudn't have said "all."
> 
>> I can't stress this enough - one must *already* be aware of the declaration to write correct code. Implicit conversions can cause trouble, as well as in, out, inout, const, and all the other storage classes discussed.
> 
> 
> True enough.  But I consider this a somewhat different class of unpredictable behavior, if only because it isn't something present in this way in any other C-like language (C# 3.0 will have lambda functions similar to this with a leading => at the call point).
> 
>> Previous discussions here frequently revolved around the need to put information in the function declaration so people have some guarantees about what the function does - const is a prime example. Another is the suggestion to move the contracts from the implementation to the declaration.
>>
>> There is no way to know what:
>>
>>     somefunc(++i);
>>
>> does without looking at the declaration for somefunc() - even without lazy evaluation.
> 
> 
> But the possibilities are rather limited, either i will be mutated or it will not.  I suppose what worries me isn't that I need to look at the declaration to determine what will happen with implicit delegate conversion, but that even looking at the declaration won't tell me what may happen.
> 
> By the same token, I actually like C's method of pass-by-reference because call-side code inspection reveals which parameters may be modified.  I think this is why I like the suggestion to retain curly braces and perhaps derive the return value automatically--it makes the contract between caller and callee explicit.
> 
> But this is a new feature and I do want to spend more time with it before making a final decision.  If nothing else, I agree that time and experience will reduce the chance of mistakes demonstrated recently, as this becomes "just another tool" we have available.
> 
> 
> Sean


Tom's suggestion is to introduce a different style of delegate for this lambda expression thingy. That would resolve the difficulties here (assuming delegates can be auto-converted to the lambda expr; per Tom's suggestion).

But for delegate itself, it sure would be nice to have:

# somefunk ({++i});

as a shorthand for

# somefunk ({return ++i;});
August 23, 2006
kris wrote:
> Walter Bright wrote:
>> kris wrote:
>>
>>> Can't do that; the char[] version have to stay.
>>
>>
>> Why?
> 
> 
> Because my Boss says so

kris, really, is it a joke?

If not, and I'm supposing you are using the mango logging package in some big D application, can you post us the performance difference you have noticed using the char[] version versus the delegate version?

---
Paolo Invernizzi
August 23, 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 );

If that's the case, then something's wrong.  If funcRetInt returns a char[], despite the name, then funcRetInt() is of type char[]. Therefore it should match this first.

> 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()
<snip>

That syntax looks confusingly like an array initialiser....

Stewart.
August 23, 2006
>>
>> func( char[] a ) vs. func( char[] delegate() dg )
>>
>> func( funcRetInt() ); vs. func( &funcRetInt );
> 
> If that's the case, then something's wrong.  If funcRetInt returns a char[], despite the name, then funcRetInt() is of type char[]. Therefore it should match this first.

This is the first example, showing a ambiguity
func( char[] a ) vs. func( char[] delegate() dg )

This is a second example, which has no relation to the first.
func( funcRetInt() ); vs. func( &funcRetInt );

And the "vs." is not a object reference. Sorry.


>> { "abc"         } => char[] delegate()
> That syntax looks confusingly like an array initialiser....

the '=>' was not a syntax proposal
"{ "abc"         }" /is of type/ "char[] delegate()"


August 23, 2006
Walter Bright wrote:
> Sean Kelly wrote:
> 
>> Walter Bright wrote:
>>
>>>
>>> 3) It is possible that the delegate can be inlined, thus eliminating any extra overhead.
>>
>>
>> Really?  Are you saying that if the receiving function is short enough the function and its delegate use may be inlined in the calling code?  I can't imagine that it would simply be inlined in the receiving function and duplicates of that wold be generated.  Or at least, DMD doesn't appear to do that now.
> 
> 
> It doesn't do it now, but it is possible to do. What an advanced compiler can do is duplicate the function into two, one gets the value arguments that have no computation, the other gets the side effect arguments and ones that involve computation as delegates.

a.k.a

foo(char[] delegate())

gets converted by the compiler into

foo(char[] delegate())
foo(char[])

???

That would be REALLY cool. But I'm not sure I trust the compiler that much.

August 23, 2006
On Wed, 23 Aug 2006 00:33:08 -0700, Paolo Invernizzi <arathorn@NOSPAM_fastwebnet.it> wrote:

> kris wrote:
>> Walter Bright wrote:
>>> kris wrote:
>>>
>>>> Can't do that; the char[] version have to stay.
>>>
>>>
>>> Why?
>>   Because my Boss says so
>
> kris, really, is it a joke?
>
> If not, and I'm supposing you are using the mango logging package in some big D application, can you post us the performance difference you have noticed using the char[] version versus the delegate version?
>
> ---
> Paolo Invernizzi


I think he was being sarcastic.

-JJR
August 23, 2006
Frank Benoit wrote:
>>> func( char[] a ) vs. func( char[] delegate() dg )
>>>
>>> func( funcRetInt() ); vs. func( &funcRetInt );
>> If that's the case, then something's wrong.  If funcRetInt returns a
>> char[], despite the name, then funcRetInt() is of type char[]. Therefore
>> it should match this first.
> 
> This is the first example, showing a ambiguity
> func( char[] a ) vs. func( char[] delegate() dg )

If one overload matches exactly, it goes without saying that that's the one that's called.  As such, the programmer would do

    func("qwert");

to call the first, and

    func({ return "qwert"; });

to call the second.  Just like in any other case.

> This is a second example, which has no relation to the first.
> func( funcRetInt() ); vs. func( &funcRetInt );

In that case, I don't get what your problem is.

> And the "vs." is not a object reference. Sorry.

Pardon?

>>> { "abc"         } => char[] delegate()
>> That syntax looks confusingly like an array initialiser....
> 
> the '=>' was not a syntax proposal
> "{ "abc"         }" /is of type/ "char[] delegate()"

The '=>' doesn't look anything like an array initialiser either.  Unless there's some other kind of array initialiser that I haven't discovered yet....

Wasn't it obvious that I was talking about the bit to the left of those two characters?

-- 
-----BEGIN GEEK CODE BLOCK-----
Version: 3.1
GCS/M d- s:-@ C++@ a->--- UB@ P+ L E@ W++@ N+++ o K-@ w++@ O? M V? PS- PE- Y? PGP- t- 5? X? R b DI? D G e++++ h-- r-- !y
------END GEEK CODE BLOCK------

My e-mail is valid but not my primary mailbox.  Please keep replies on the 'group where everyone may benefit.
August 23, 2006
John Reimer wrote:
> On Wed, 23 Aug 2006 00:33:08 -0700, Paolo Invernizzi  <arathorn@NOSPAM_fastwebnet.it> wrote:
> 
>> kris wrote:
>>
>>> Walter Bright wrote:
>>>
>>>> kris wrote:
>>>>
>>>>> Can't do that; the char[] version have to stay.
>>>>
>>>>
>>>>
>>>> Why?
>>>
>>>   Because my Boss says so
>>
>>
>> kris, really, is it a joke?
>>
>> If not, and I'm supposing you are using the mango logging package in  some big D application, can you post us the performance difference you  have noticed using the char[] version versus the delegate version?
>>
>> ---
>> Paolo Invernizzi
> 
> 
> 
> I think he was being sarcastic.
> 
> -JJR

No sarcasm required. The recent development of deliberately introducing ambiguity into the language warrants serious concern. It's something that should, and does, trouble most of us quite deeply (judging by the responses).

As for performance differences, I suspect you're missing the bigger picture, Paolo? I'll try to draw one: you may have noticed my concern about delegates and heap-based frames? If so, you'll probably have seen a reponse from Walter noting that specific types of expr can probably be identified as not escaping the hosting function? There's a gaping hole in that marketing-style claim, and you should treat it with a heathly dose of suspicion.

For example; I'd like to use delegates as a nice clean shortcut for logging. Instead of writing

# if (log.enabled (log.Trace))
#     log.trace (sprint ("my log message %d, %s", i, s));

I'd instead like to write this

# log.trace ({return sprint ("my log message %d, %s", i, s);});


Makes sense, right? We don't want to invoke the cost of the sprint() function unless tracing is enabled. Note that these are guaranteed to be synchronous callbacks. The delegates are not "saved for later" like a gui delegate might be. Thus, the frame of the hosting function should be stack-based.

However, because the callback references local variables, there's no guarantee the host-frame will not be allocated on the heap instead. This is the issue we'd been trying to address in another thread. Namely, if there's going to be ambiguity over whether a delegate is invoked syncrhonously or asynchronously, we probably need a way to tell the compiler what's what (rather than let it make the wrong choice).

In simplistic terms, if I have a function like so (note that there's no delegate intended here):

# real somefunk (real x, real y)
# {
# 	if (x is real.nan || y is real.nan)
#           log.error (sprint ("invalid args %s, %s", x, y));
#
#         // do something with x & y
# }

It's quite possible that the frame for this will be heap-allocated, even though the logger invocation is not intending to use delegates at all. The damn compiler might go and turn the char[] expr into a sneaky delegate, and then decide it needs a heap-frame instead of a stack one. That, my friend, is a serious performance issue (heap allocation each and every time somefunk() is called), and it's all hidden behind a plush blue-velvet curtain.


Supposing we actually *want* to use a delegate in this situation? e.g.

# real somefunk (real x, real y)
# {
# 	if (x is real.nan || y is real.nan)
#           log.error ({return sprint ("invalid args %s, %s", x, y);});
#
#         // do something with x & y
# }

We still need some manner in which to tell the compiler that the callback is synchronous, rather than asynchronous, and that it should not be trying to allocate a heap-frame for this function. Again, this is what the other thread was discussing in depth. One of the ideas put-forward there was to somehow mark the hosting function as being a synchronous-host; something like this

# scope real somefunk (real x, real y);

Another opinion was to mark the /usage/ of the delegate itself, the logger in this case, with something akin to the following

# void error (scope char[] delegate());


Each of these have their relative pros and cons. What *is* evident, is that the compiler cannot be trusted to make the appropriate decision. In contrast, it's quite likely to make a highly conservative, and very poor, decision. Implied behaviour is often entirely the wrong thing to do and, without a means for manual control, will quickly become a most serious problem.

I hope you can see where the recent ambiguities lead to? You can easily wind up with unexpected (and unwanted) delegates, which may well lead to unexpected (and unwanted) heap-based frames. To resolve, the ambiguities must be removed. Additionally, some *dependable* mechanism should be put in place to manage (or override) how & when heap-based frames are allocated.






August 23, 2006

Stewart Gordon wrote:
> Frank Benoit wrote:
> 
>>>> func( char[] a ) vs. func( char[] delegate() dg )
>>>>
>>>> func( funcRetInt() ); vs. func( &funcRetInt );
>>>
>>> If that's the case, then something's wrong.  If funcRetInt returns a
>>> char[], despite the name, then funcRetInt() is of type char[]. Therefore
>>> it should match this first.
>>
>>
>> This is the first example, showing a ambiguity
>> func( char[] a ) vs. func( char[] delegate() dg )
> 
> 
> If one overload matches exactly, it goes without saying that that's the one that's called.  As such, the programmer would do
> 
>     func("qwert");
> 
> to call the first, and
> 
>     func({ return "qwert"; });
> 
> to call the second.  Just like in any other case.

That's what I would have thought as well, but its already been said that these cases are considered ambiguous...  For example, the following code:

# module lazy0 ;
#
# import std .stdio ;
#
# void foo (char[] txt) {
#   writefln(`%s`c, txt);
# }
#
# void foo (char[] delegate () expr) {
#   writefln(`%s`c, expr());
# }
#
# void main () {
#   foo(`Alpha`c);
# }

Produces this error:

# lazy0.d(14): function lazy0.foo called with argument types:
#         (char[5])
# matches both:
#         lazy0.foo(char[])
# and:
#         lazy0.foo(char[] delegate())


>> This is a second example, which has no relation to the first.
>> func( funcRetInt() ); vs. func( &funcRetInt );
> 
> 
> In that case, I don't get what your problem is.
> 
>> And the "vs." is not a object reference. Sorry.
> 
> 
> Pardon?
> 
>>>> { "abc"         } => char[] delegate()
>>>
>>> That syntax looks confusingly like an array initialiser....
>>
>>
>> the '=>' was not a syntax proposal
>> "{ "abc"         }" /is of type/ "char[] delegate()"
> 
> 
> The '=>' doesn't look anything like an array initialiser either.  Unless there's some other kind of array initialiser that I haven't discovered yet....
> Wasn't it obvious that I was talking about the bit to the left of those two characters?
> 

The only thing I can think of is the PHP format:
# $foo = array(
#   'Alpha' => 3 ,
#   'Beta'  => 6
# );

As far as I know, the D way is to use a colon (:) for this sort of thing.  Although the bit on the left doesn't look like an array initialiser either, in terms of D, as they use brackets rather than braces.
# const int[] foo = [3, 6, 9] ;

Static structure literals do use braces, however, so there's still some ambiguity.

# struct S { int foo; float bar; }
#
# static S MyS = {42, 3.14} ;

-- Chris Nicholson-Sauls
August 24, 2006
kris wrote:

>> On Wed, 23 Aug 2006 00:33:08 -0700, Paolo Invernizzi  <arathorn@NOSPAM_fastwebnet.it> wrote:
>> can you post us the performance difference you  have noticed using the char[] version versus the delegate version?
> 
> As for performance differences, I suspect you're missing the bigger picture, Paolo?

I've followed the discussion.

While I agree that ambiguities must be avoided if possible, and that compiler-made decision are sometimes frustrating (but we all are happy about class-all-virtual-methods!), I was just curious of the performance impact of such a change...

---
Paolo Invernizzi