September 01, 2006
Derek Parnell wrote:
> Damn! I didn't know that. What's the rationale for such a seemingly
> arbitrary restriction?

No virtual functions for structs!
September 01, 2006
On Thu, 31 Aug 2006 20:32:01 -0700, Walter Bright wrote:

> Derek Parnell wrote:
>> Damn! I didn't know that. What's the rationale for such a seemingly arbitrary restriction?
> 
> No virtual functions for structs!

Ok, that meant nothing to me until I translated it from the OO jargon. Here is my attempt at converting Walter's answer into human speech ;-)

The 'format' function examines each parameter supplied to it in order to convert it into some form of string. When it comes to a class object, it casts the object instance to 'Object' and then calls Object.toString(), which through the magic of 'virtual functions' actually gets around to calling the parameter's own toString() method if it exists. 'virtual functions' is the mechanism in which derived classes can have their methods called via reference to their parent class. Now structs are not derived from anything and thus there cannot be a generic toString() method in a struct parent. So the 'format' function doesn't know which struct toString() to call and it throws the exception instead.

I guess that when D eventually gets runtime reflection we will be able to improve 'format' to check if the struct parameter passed to it has a toString method and thus call it.

-- 
Derek
(skype: derek.j.parnell)
Melbourne, Australia
"Down with mediocrity!"
1/09/2006 2:29:34 PM
September 01, 2006
Derek Parnell wrote:
> I guess that when D eventually gets runtime reflection we will be able to
> improve 'format' to check if the struct parameter passed to it has a
> toString method and thus call it.

It could also be done with variadic templates.

-- 
Kirk McDonald
Pyd: Wrapping Python with D
http://pyd.dsource.org
September 01, 2006
Walter Bright wrote:
> The implicit conversion to delegate just broke too much. Instead, I'm trying out Tom S.'s suggestion of using a 'lazy' parameter storage class.
> 
> http://www.digitalmars.com/d/changelog.html

Hmm, I am afraid I have to admit I'm a little bit puzzled by this lazy thing.

Let's say we have:

void dotimes(int count, lazy void exp)
{
  for (int i = 0; i < count; i++)
  {
    exp();
  }
}

and I can call it like this:

int x;
dotimes(5, x++);

And it works as expected and x end up being 5.

But shouldn't I also be allowed to pass in a delegate of the appropriate type

dotimes(5, {writefln(x); x++;});

A funny thing happens: nothing. The loop in dotimes does get executed 5 times but delegate passed in isn't called?


September 01, 2006
Derek Parnell wrote:
> On Thu, 31 Aug 2006 20:32:01 -0700, Walter Bright wrote:
> 
>> Derek Parnell wrote:
>>> Damn! I didn't know that. What's the rationale for such a seemingly
>>> arbitrary restriction?
>> No virtual functions for structs!
> 
> Ok, that meant nothing to me until I translated it from the OO jargon. Here
> is my attempt at converting Walter's answer into human speech ;-)
> 
> The 'format' function examines each parameter supplied to it in order to
> convert it into some form of string. When it comes to a class object, it
> casts the object instance to 'Object' and then calls Object.toString(),
> which through the magic of 'virtual functions' actually gets around to
> calling the parameter's own toString() method if it exists. 'virtual
> functions' is the mechanism in which derived classes can have their methods
> called via reference to their parent class. Now structs are not derived
> from anything and thus there cannot be a generic toString() method in a
> struct parent. So the 'format' function doesn't know which struct
> toString() to call and it throws the exception instead. 
> 
> I guess that when D eventually gets runtime reflection we will be able to
> improve 'format' to check if the struct parameter passed to it has a
> toString method and thus call it.
> 

Another alternative is a syntax using the concatenation operator:
  writefln("through a voltage of" ~ v);
This can be done already with overloading of opCat_r and opCat, but it's way less than ideal since it has to be done for every new type. Or D could be changed so that '~' itself would try to call toString() if it existed.

Yet another alternative is for any type that has a toString() member function to be implicitly castable to string (aka "char[]"). Then writefln would be declared as a typesafe variadic function of (string[] strs ...). It would also be necessary for implicit casting to work with typesafe variadic function calls, which seems it doesn't (I wonder if that's by design?). And it would be necessary for primitives to have toString "member functions" (it's unusual, but I don't see any problem with that. Primitives already have properties for example). Usage would be:
  writeln("X is ", 1.2345 );
Note we would lose the formatting capabilities(no "f"). To do formatting we could do for example:
  writeln("X is ", (1.2345).toString("%1.2d") );

Just some wacky ideas. :P

-- 
Bruno Medeiros - MSc in CS/E student
http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
September 01, 2006

Ivan Senji wrote:
> Walter Bright wrote:
> 
>> The implicit conversion to delegate just broke too much. Instead, I'm trying out Tom S.'s suggestion of using a 'lazy' parameter storage class.
>>
>> http://www.digitalmars.com/d/changelog.html
> 
> 
> Hmm, I am afraid I have to admit I'm a little bit puzzled by this lazy thing.
> 
> Let's say we have:
> 
> void dotimes(int count, lazy void exp)
> {
>   for (int i = 0; i < count; i++)
>   {
>     exp();
>   }
> }
> 
> and I can call it like this:
> 
> int x;
> dotimes(5, x++);
> 
> And it works as expected and x end up being 5.
> 
> But shouldn't I also be allowed to pass in a delegate of the appropriate type
> 
> dotimes(5, {writefln(x); x++;});
> 
> A funny thing happens: nothing. The loop in dotimes does get executed 5 times but delegate passed in isn't called?
> 
> 

This is because anonymous delegates are themselves expressions, and so your 'exp()' just evaluates to the delegate itself -- it does not invoke it.  However, to achieve what you were trying to do, just make use of parentheses and the comma operator.  Also, you may provide an overload that takes a lazy delegate.  Here is my own test program, which worked without issue:

# module lazy1 ;
#
# import std .stdio ;
#
# void dotimes (int count, lazy void exp) {
#   while (count--) exp();
# }
#
# void dotimes (int count, lazy void delegate() exp) {
#   while (count--) exp()();
# }
#
# void main () {
#   int x;
#   dotimes(5, x++);
#   writefln("x is now: ", x);
#
#   int y;
#   dotimes(5, (writefln("[y] ", y), y++));
#   writefln("y is now: ", y);
#
#   int z;
#   dotimes(5, {writefln("[z] ", z); z++;});
#   writefln("z is now: ", z);
# }

-- Chris Nicholson-Sauls
September 01, 2006
Walter Bright wrote:
> Russ Lewis wrote:
> 
>> Ah, I was wondering if that was what you meant.  What I was suggesting was that the code
>>     lazy x+y
>> would just be syntax sugar for
>>     delegate int() { return x+y; }
>>
>> So, to expand on the previous example:
>>   void foo(int delegate() dg) {...}
>>   void foo(int i) {...}
>>   void bar(int x,int y) {
>>     foo(lazy x+y); // calls delegate version
>>     foo(x+y);      // calls int version
>>   }
> 
> Ok, that can work. But I don't see an application for it.

Heh, sems like I'm having a lot of trouble explaining myself here.

I'm not trying to argue that overloading a function with both delegate and literal versions has a lot of value.  I was just trying to say that the syntax I was proposing was not ambiguous.

My argument is that it would be good to make laziness explicit in the caller, so that the caller of the code knows what he can expect.
September 01, 2006
Russ Lewis wrote:
> Walter Bright wrote:
>> Russ Lewis wrote:
>>
>>> Ah, I was wondering if that was what you meant.  What I was suggesting was that the code
>>>     lazy x+y
>>> would just be syntax sugar for
>>>     delegate int() { return x+y; }
>>>
>>> So, to expand on the previous example:
>>>   void foo(int delegate() dg) {...}
>>>   void foo(int i) {...}
>>>   void bar(int x,int y) {
>>>     foo(lazy x+y); // calls delegate version
>>>     foo(x+y);      // calls int version
>>>   }
>>
>> Ok, that can work. But I don't see an application for it.
> 
> Heh, sems like I'm having a lot of trouble explaining myself here.
> 
> I'm not trying to argue that overloading a function with both delegate and literal versions has a lot of value.  I was just trying to say that the syntax I was proposing was not ambiguous.
> 
> My argument is that it would be good to make laziness explicit in the caller, so that the caller of the code knows what he can expect.

I believe this is the same as Derek's suggestion that in/out/inout be specified at the call side as well.


Sean
September 01, 2006
Sean Kelly wrote:
> Russ Lewis wrote:
>> Walter Bright wrote:
>>> Russ Lewis wrote:
>>>
>>>> Ah, I was wondering if that was what you meant.  What I was suggesting was that the code
>>>>     lazy x+y
>>>> would just be syntax sugar for
>>>>     delegate int() { return x+y; }
>>>>
>>>> So, to expand on the previous example:
>>>>   void foo(int delegate() dg) {...}
>>>>   void foo(int i) {...}
>>>>   void bar(int x,int y) {
>>>>     foo(lazy x+y); // calls delegate version
>>>>     foo(x+y);      // calls int version
>>>>   }
>>>
>>> Ok, that can work. But I don't see an application for it.
>>
>> Heh, sems like I'm having a lot of trouble explaining myself here.
>>
>> I'm not trying to argue that overloading a function with both delegate and literal versions has a lot of value.  I was just trying to say that the syntax I was proposing was not ambiguous.

Ok.

>> My argument is that it would be good to make laziness explicit in the caller, so that the caller of the code knows what he can expect.
> 
> I believe this is the same as Derek's suggestion that in/out/inout be specified at the call side as well.

Yes, it would be the same. But I think the interface should be set by the definition, not the use. That's the usual practice with programming languages, and I've never seen it raised as an issue before.
September 01, 2006
Sean Kelly wrote:
> Russ Lewis wrote:
>> Walter Bright wrote:
>>> Russ Lewis wrote:
>>>
>>>> Ah, I was wondering if that was what you meant.  What I was suggesting was that the code
>>>>     lazy x+y
>>>> would just be syntax sugar for
>>>>     delegate int() { return x+y; }
>>>>
>>>> So, to expand on the previous example:
>>>>   void foo(int delegate() dg) {...}
>>>>   void foo(int i) {...}
>>>>   void bar(int x,int y) {
>>>>     foo(lazy x+y); // calls delegate version
>>>>     foo(x+y);      // calls int version
>>>>   }
>>>
>>> Ok, that can work. But I don't see an application for it.
>>
>> Heh, sems like I'm having a lot of trouble explaining myself here.
>>
>> I'm not trying to argue that overloading a function with both delegate and literal versions has a lot of value.  I was just trying to say that the syntax I was proposing was not ambiguous.

Ok.

>> My argument is that it would be good to make laziness explicit in the caller, so that the caller of the code knows what he can expect.
> 
> I believe this is the same as Derek's suggestion that in/out/inout be specified at the call side as well.

Yes, it would be the same. But I think the interface should be set by the definition, not the use. That's the usual practice with programming languages, and I've never seen it raised as an issue before.