Delegates (agian)

I’ve been thinking about delegates and IronRuby.

After playing around with trying to work with a delegate in Ruby (procs,
lambdas and god-knows-what) I’ve come to the conclusion that delegates
have to be supported in a more fundamental way than trying to make a
block work as a delegate.

First off, I think IR has to support adding and removing delegates via
‘+=’ and ‘-=’. That’s how every other .NET language does it (IronPython
included). It doesn’t seem right to use ‘<<’ because there isn’t an
equivalent ‘>>’ (though I suppose you could define one). Anyway, you
have to be able to remove a delegate as well as add one.

Secondly, if you look at the way C# and VB address delegates, they use
syntactic kludges to get round the fact that delegates are function
pointers and are not to be invoked directly. C# uses the method name
with no parentheses and VB uses AddressOf. There isn’t really an
equivalent of a function pointer in Ruby. The most natural way of doing
this, I guess is to use a symbol:

x += EventHandler.new (:y)

However, this doesn’t seem right because a) it won’t run and b) a symbol
isn’t a function pointer. So possibly a better way might be to create a
new class ‘RubyDelegate’ (deriving from Delegate/MulticastDelegate)
which takes the object and method name (or symbol) and returns a real
.NET CLR delegate:

x += EventHandler(RubyDelegate.new (self, “y”))

or

x += RubyDelegate.new (self, “y”)

Third: I notice that all the other languages (C# and VB at any rate, I
can’t speak for things like F#) futz around with special delegate
keywords to help as well as fooling around with the invokation syntax.

Fourth: Delegates are plumbed into .NET at a pretty deep level. Most
people, most of the time, don’t see them. But they are there - and I’ve
used them quite a bit in communication systems. It seems to me that you
have to recogize them in IronRuby at a similarly primitive level and not
try to pretend that a delegate is equivalent to a block.

A delegate is a function pointer. A block isn’t.

Dermot

Dermot H. wrote:

equivalent ‘>>’ (though I suppose you could define one). Anyway, you
have to be able to remove a delegate as well as add one.

A problem you’ve missed here is that x += y and x -= y are (always)
equivalent in Ruby to x = x + y and x = x - y. Unless + or - mutate the
original object, now mutation will occur.

Even in the case where they may be attributes:

class Foo
attr_accessor :a
end

f = Foo.new
f.a = 1
f.a += 2

equivalent to

f.a = f.a + 2

The only way this might be able to work is if you defined + against a
delegate to return the full collection of delegates, and then assignment
updates the original list…

class DelegateList
def initialize(*delegates)
@delegates = delegates
end

def +(other)
DelegateList.new(other, *@delegates)
end
end

f = Foo.new
f.a = DelegateList.new(some_delegate)
f.a += some_other_delegate

Thoughts? We’ve had similar questions about how to handle event listener
registration in JRuby, though there’s no legacy operator syntax to
support in our case.

  • Charlie

On 10/25/07, Charles Oliver N. [email protected] wrote:

The only way this might be able to work is if you defined + against a
delegate to return the full collection of delegates, and then assignment
updates the original list…

This doesn’t work for CLR events in the general case because events are
opaque that way – you can’t get a list of delegates that have been
added to
the event.

In general, any object that is somehow callable is a “function pointer”
in DLR (this includes CLR delegates, CLR events, Ruby
proc/lambda/Method, Python functions, JScript functions, etc.). All of
them should just work in IronRuby.

The main methods that would IronRuby see on a CLR are add, remove and
call. Others are just syntactic sugar and aliases (<< is alias for add).
<< is as right for events as it is for Ruby Array IMO (there is also no
method >> on array, it’s Array#delete). += -= are a little bit hacky
from Ruby’s point of view IMO. They are more discoverable for a CLR guys
though. So it might be good to support them as well.

I don’t think we need RubyDelegate, you can write

x << method(:y)
x << C.instance_method(:y).bind(obj)

or

x += method(:y)
x += C.instance_method(:y).bind(obj)
x -= method(:y)

if you will.

Blocks/procs should also work:

x { puts ‘on click’ }
x << lambda { puts ‘on click’ }

Tomas

x << C.instance_method(:y).bind(obj)

this piece of code does not add any method to anyone. It just passes
C’s implementation of method “y” bound to object “obj” as an argument
to object “x”'s “<<” method. No method adding, just indirection.

At the DLR level we can basically convert any callable object into a
delegate. This is done by creating a dynamic method (that matches the
delegates signature) that contains an embedded DynamicSite which
performs a CallAction. That call action works through our
IDynamicObject implementations (for complex language defined types),
through language specific binders (Python wants to make newable-things
callable), or through the base binder which provides the implementation
for known type-systems (CLR types, COM types in the future, maybe
IReflect/IExpando in the future?).

So anything can be a delegate (it just might throw at call time)!

(you might see 2 of these, I don’t think rubyforge liked my e-mail
address before)

I don’t think we need RubyDelegate, you can write

x << method(:y)
x << C.instance_method(:y).bind(obj)

or

x += method(:y)
x += C.instance_method(:y).bind(obj)
x -= method(:y)

if you will.

Blocks/procs should also work:

x { puts ‘on click’ }
x << lambda { puts ‘on click’ }

Tomas

This isn’t really what I mean. You need to be able to provide
syntactically a delegate reference.

So in C#, we have x += new EventHandler(mymethod)

and in VB (I’m a bit rusty but something like this):

x += new EventHandler(AddressOf mymethod)

it’s not the same as adding a method to an object. The object already
has the method. What you need to to do is add the delegate (a
typesafe function pointer) into the delegate chain.

I’m not an expert in any way in what you are doing in the DLR, but it
doesn’t seem to me that this does it. A delegate is a different thing
from a method object. ‘method’ returns a method object which isn’t the
same as a delegate (an instance of the Delegate class) - at least, I
can’t add it into the delegate chain with the current version of IR.

Are you saying that ‘method’ will return a delegate? I’m not clear on
this at all.

Dermot

Charles Oliver N. wrote:

Dermot H. wrote:

equivalent ‘>>’ (though I suppose you could define one). Anyway, you
have to be able to remove a delegate as well as add one.

A problem you’ve missed here is that x += y and x -= y are (always)
equivalent in Ruby to x = x + y and x = x - y. Unless + or - mutate the
original object, now mutation will occur.

In .NET C# etc, the += and -= for delegates don’t really mean the same
as they do in other contexts. They means ‘add/remove’ a delegate
into/from the delegate queue. You can’t say

x = new EventHandler(mymethod)

all you can do is add/remove. It’s another example of syntactic kluging
IMO. It does work well in practise, though.

Thoughts? We’ve had similar questions about how to handle event listener
registration in JRuby, though there’s no legacy operator syntax to
support in our case.

Yes - I suppose so. You still have to add the address of the listener
into the system in some way. Similar problem, but I think delegates are
a bit trickier - they aren’t just function pointers; they carry other
information as well. All .NET languages seem to treat them specially.

Dermot

On 10/25/07, Dermot H. [email protected] wrote:

You can’t say

x = new EventHandler(mymethod)

I was trying to avoid writing this, but my inner pedant simply won’t let
go. There’s a difference between delegates and events. A delegate is
simply a bound function pointer, and it’s perfectly legal to do a
straightforward assignment as above. An event is basically a published
endpoint to which you subscribe and unsubscribe by adding and removing
your
delegate. The two are obviously related but not the same, and what we
seem
to be talking about here specifically involves events.

I feel much better now. :slight_smile:

On 10/25/07, Dermot H. [email protected] wrote:

In .NET C# etc, the += and -= for delegates don’t really mean the same
as they do in other contexts. They means ‘add/remove’ a delegate
into/from the delegate queue. You can’t say

x = new EventHandler(mymethod)

all you can do is add/remove. It’s another example of syntactic kluging
IMO. It does work well in practise, though.

This is actually a fundamental property of events on a CLR / IL level.
Events are exposed as a pair of functions to add a handler and remove a
handler. The reason for this is efficiency; if every Control in a
Windows
Forms application defined a slot for each of its events, you’d have an
immense number of slots defined – even though most of them would
effectively have a value of null.

Dermot H. wrote:

So in C#, we have x += new EventHandler(mymethod)

and in VB (I’m a bit rusty but something like this):

x += new EventHandler(AddressOf mymethod)

The syntax for VB event handlers is

AddHandler x.myevent, AddressOf mymethod

assuming that the event being handled here is myevent.

I don’t know if it’s relevant or not, but the VB Windows Forms designer
doesn’t generate anything like this anyway. It looks like:

Private Sub x_myevent() Handles x.myevent
End Sub

The compiler wires up the delegates.

Joe Chung wrote:

The syntax for VB event handlers is

AddHandler x.myevent, AddressOf mymethod

assuming that the event being handled here is myevent.

Thanks! My VB is rustier than I’d assumed :slight_smile:

Dermot

Eric N. wrote:

I don’t know if it’s relevant or not, but the VB Windows Forms designer
doesn’t generate anything like this anyway. It looks like:

Private Sub x_myevent() Handles x.myevent
End Sub

The compiler wires up the delegates.

Your code is syntactic sugar that generates

AddHandler Me.myevent, New EventHandler(AddressOf Me.x_myevent)

You can check this out for yourself with Reflector.

It is relevant, because here are two examples (within the same language
even) of language-specific syntax for event handling delegates. I think
it would be okay if IronRuby had Ruby-like syntax for delegates as well.

Curt H. wrote:

On 10/25/07, Dermot H. [email protected] wrote:

I was trying to avoid writing this, but my inner pedant simply won’t let
go. There’s a difference between delegates and events. A delegate is
simply a bound function pointer, and it’s perfectly legal to do a
straightforward assignment as above. An event is basically a published
endpoint to which you subscribe and unsubscribe by adding and removing
your
delegate. The two are obviously related but not the same, and what we
seem
to be talking about here specifically involves events.

I feel much better now. :slight_smile:

Yes. I’ve lumped them together … there are two parts to this. First,
the add-assignment (which isn’t an assignment or an addition, but we’ll
let that pass). I don’t really care whether this is a traditional += or
a Ruby like << (which is more logical, I now think) so long as we have a
delegate removal operator as well.

The second part is the delegate constructor syntax, that is how you pass
the name of the event handler itself to the System::EventHandler
constructor. All I want is a reasonable way to doing this without
involving blocks. I’d be quite happy to settle for this:

x << System::EventHandler(self, :y)

whatever it is, the arguments have to be acceptable to the .NET
System::EventHandler API. What I’ve been trying to point out is that
different languages (VB, C#, IronPython, …) do this in slightly
different ways, but whatever they do, they don’t use anything
convoluted. However they do it, it’s clear what’s going on.

Dermot