www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - lazy evals + parameter passing

reply =?ISO-8859-1?Q?Jari-Matti_M=E4kel=E4?= <jmjmak utu.fi.invalid> writes:
Hi, I've been away for a while working with Ruby.

It seems the D spec is now extended with lazy evaluation of function
arguments. Very nice indeed.

I tried to search the ng but haven't seen any mention about 'yield' in
the context of lazy evals. Currently I think the implementation is only
half ready or then I've forgotten how to program in D :) See, some Ruby here

def inject(list, n)
 list.each { |v| n = yield(n, v) }
 return n
end

def sum(list)
 return inject(list, 0) { |n, v| n + v }
end

sum( [1,2,3] )

=> 6

If I understand this correctly, currently D only supports yield's
without arguments when used with anonymous delegates (Ok, it's lacking
multiple return values too, but those can be simulated easily). The
point is that the caller _can_ actually send parameters to the delegate,
but there's no way to receive arguments from the sender. Is it too
expensive to extend current lazy evals with arguments in a systems
programming language like D? Does it even require the use of tuples?

I think it's fully possible by introducing |par1, ..., parn | syntax so that

{ | a, b | a+b }

would be the same as

int _anonymous(inout a, inout b) {
  return a+b;
}

What do you think? Does it look ugly? Doesn't it offer a great deal of
new functionality. I think we really NEED this - please Walter, make it
pre-1.0 :)
Sep 04 2006
parent reply Chris Nicholson-Sauls <ibisbasenji gmail.com> writes:
Jari-Matti Mäkelä wrote:
 Hi, I've been away for a while working with Ruby.
 
 It seems the D spec is now extended with lazy evaluation of function
 arguments. Very nice indeed.
 
 I tried to search the ng but haven't seen any mention about 'yield' in
 the context of lazy evals. Currently I think the implementation is only
 half ready or then I've forgotten how to program in D :) See, some Ruby here
 
 def inject(list, n)
  list.each { |v| n = yield(n, v) }
  return n
 end
 
 def sum(list)
  return inject(list, 0) { |n, v| n + v }
 end
 
 sum( [1,2,3] )
 
 => 6
 
 If I understand this correctly, currently D only supports yield's
 without arguments when used with anonymous delegates (Ok, it's lacking
 multiple return values too, but those can be simulated easily). The
 point is that the caller _can_ actually send parameters to the delegate,
 but there's no way to receive arguments from the sender. Is it too
 expensive to extend current lazy evals with arguments in a systems
 programming language like D? Does it even require the use of tuples?
 
 I think it's fully possible by introducing |par1, ..., parn | syntax so that
 
 { | a, b | a+b }
 
 would be the same as
 
 int _anonymous(inout a, inout b) {
   return a+b;
 }
 
 What do you think? Does it look ugly? Doesn't it offer a great deal of
 new functionality. I think we really NEED this - please Walter, make it
 pre-1.0 :)

First, D's interesting new Lazy feature is really not like Ruby's Block feature at all -- although I for one wouldn't mind seeing something like yielding show up in D. Instead, Lazy is primarily about affirming that an expression will not be evaluated until it is neccessary to do so. In other words, that you could for example pass a complicated string expression to a function which MAY or MAY NOT output that string somewhere. Lazy evaluation promises you that the string won't get processed unless that function DOES output it. That noted, of course, they do behave essentially as delegates (and internally ARE delegates I believe) and so the given expression MAY be evaluated multiple times. One would have to note the documentation for the function in question before relying on or protecting against any side effects. Second, the new anonymous delegate syntax already gives us more-or-less what you want except for two missing items: a delegate body containing nothing but an expression should get an implicit 'return ... ;' wrapped around it, yielding capability. { | a, b | a + b } is actually (a,b){ a + b } I'm also not positive if type inferance would handle this case or not, so it may more correctly be: (int a, int b){ a + b } -- Chris Nicholson-Sauls
Sep 04 2006
parent =?ISO-8859-1?Q?Jari-Matti_M=E4kel=E4?= <jmjmak utu.fi.invalid> writes:
Chris Nicholson-Sauls wrote:
 Jari-Matti Mäkelä wrote:
 I think it's fully possible by introducing |par1, ..., parn | syntax
 so that

 { | a, b | a+b }

 would be the same as

 int _anonymous(inout a, inout b) {
   return a+b;
 }

 What do you think? Does it look ugly? Doesn't it offer a great deal of
 new functionality. I think we really NEED this - please Walter, make it
 pre-1.0 :)

First, D's interesting new Lazy feature is really not like Ruby's Block feature at all

No, but it's pretty near.
 Instead, Lazy is primarily about affirming that
 an expression will not be evaluated until it is neccessary to do so.  In
 other words, that you could for example pass a complicated string
 expression to a function which MAY or MAY NOT output that string
 somewhere.  Lazy evaluation promises you that the string won't get
 processed unless that function DOES output it.

 That noted, of course, they do behave essentially as delegates (and
 internally ARE delegates I believe) and so the given expression MAY be
 evaluated multiple times.  One would have to note the documentation for
 the function in question before relying on or protecting against any
 side effects.

I guess this has been discussed before, but I'm still curious - why are they separate? Is it because there is no syntax for parameter passing or does it cause extra overhead to support full features of delegates when only a small subset is mostly needed?
 Second, the new anonymous delegate syntax already gives us more-or-less
 what you want except for two missing items: a delegate body containing
 nothing but an expression should get an implicit 'return ... ;' wrapped
 around it, yielding capability.
 
     { | a, b | a + b }
 is actually
     (a,b){ a + b }

Ah, thanks. It was a bit hidden in the docs but now I found it. This feature really rocks.
 I'm also not positive if type inferance would handle this case or not,
 so it may more correctly be:
     (int a, int b){ a + b }

This one works.
Sep 04 2006