Forums

Event declarations and firing Events

General discussion about Cobra. Releases and general news will also be posted here.
Feel free to ask questions or just say "Hello".

Event declarations and firing Events

Postby hopscc » Fri Jul 25, 2008 10:58 pm

Did you have any syntax in mind for Event declaration and for firing/Raising Events ?

Code: Select all
# A Rat is like a Mouse but bigger and hairier with a longer naked tail
sig RatEventHandler(sender as Object, args as RatEventArgs)   # exists - signature for an eventHandler

# event <eventId> as <sigId>            #-- new syntax for event?
event  ratEvent as RatEventHandler

def onRatEvent( args asRatEventArgs)    # fireRatEvent invoking handlers/Observers
# lowlvl detail like C#
    h as RatEventHandler? = RatEvent
    if h
        raise h, this, args
# OR hide detail handling -  statementish
    # raise <event>, <eventHandler commaSep argList>
    raise ratEvent, this, args             # trailing expr after eventname are arg vals to pass to handler/delegates
# OR functionish
    # raise <eventId>( <eventHandler commaSep argList>)
    raise ratEvent(this, args)
hopscc
 
Posts: 632
Location: New Plymouth, Taranaki, New Zealand

Re: Event declarations and firing Events

Postby Charles » Sat Jul 26, 2008 7:24 pm

Yes, in fact I have a workspace with outstanding changes for this that I need to finish up. I also like the conveniences that Visual Basic (VB) provides in this area such as "handles ...". Occasionally, VB does something more conveniently than C#. But the initial Cobra version will be very similar to C#.

One difference with C# is that instead of "if someEvent, someEvent(args)" you'll write "raise someEvent(args)" as you speculated in your message. The nil check will be implicit.

It is interesting to note that since only an object can raise its own events, the first arg is always this. Like the "if someEvent" it therefore becomes somewhat redundant. So just thinking out loud here:
Code: Select all
raise .clicked, EventArgs()

# furthermore, if the argument types have default constructors and
# those are satisfactory to you:
raise .clicked


I'm starting to like that. It's clean and the implied semantics are simple and consistent (always check for nil, always pass this, ...).

-Chuck
Charles
 
Posts: 2515
Location: Los Angeles, CA

Re: Event declarations and firing Events

Postby hopscc » Mon Jul 28, 2008 5:40 am

What if you need/want to fake out the receivers (Observers/handlers)
The event is always raised by Object X but the receivers need to get another object e.g. X.container? (contrived to be sure but sometime necessary)

I'd suggest you allow full specification of the 2 args as an option at least..

Looking at the syntax possibilities I'm quite enamoured with the functionish version
raise eventName( sender, eventArgs) # or raise eventName or raise eventName( eventArgs)

It just seems tidier ( probably cos just looks more familiar..)
hopscc
 
Posts: 632
Location: New Plymouth, Taranaki, New Zealand

Re: Event declarations and firing Events

Postby Charles » Tue Jul 29, 2008 3:07 am

So then we'd have:
raise .clicked
# this and EventArgs() assumed

raise .clicked(this)
# EventArgs() assumed

raise .clicked(this, EventArgs())
# full specification

raise .clicked(.container)
# specifying a fake sender; EventArgs() assumed

raise .clicked(EventArgs())
# this assumed

Regarding implementation for the last 2 cases where the call is one argument short, the compiler would try:
(1) with "this" prepended to the argument list, and
(2) with the last argument default instance appended to the argument list

If exactly one works, it's good. If both or neither work, an error.

In practice, event argument classes inherit from EventArgs and the classes that raise events do not. So it shouldn't be a problem.

I really don't want to pass "this", but I understand your concern that you may need to pass something on occasion.

I have mixed feelings about using the "call/parens" style. The "listen" and "ignore" statements (already implemented) are comma separated as well as other keyword statements such as "assert", "print" and "trace":
listen saveButton.onClick, ref .save
ignore saveButton.onClick, ref .save

print x, y

trace a, b

assert x < y, z

raise .clicked
# this and EventArgs() assumed

raise .clicked, this
# EventArgs() assumed

raise .clicked, this, EventArgs()
# full specification

raise .clicked, .container
# specifying a fake sender; EventArgs() assumed

raise .clicked, EventArgs()
# this assumed

# most frequent cases will be these two:
raise .something
raise .something, MyEventArgs(a, b)

And it might be easier to explain the special treatment of arguments for a "raise" statement than a call.

-Chuck
Charles
 
Posts: 2515
Location: Los Angeles, CA

Re: Event declarations and firing Events

Postby hopscc » Tue Jul 29, 2008 6:37 am

Not exactly. Too complex. Thinking we'd have 3 cases Only
Code: Select all
# no args
raise .clicked
# no this and EventArgs() at all ( null arg EventHandler)

# 1 arg = explicit eventArgs
raise .clicked(EventArgs())
# this assumed

#Otherwise full spec
raise .clicked(this, EventArgs())
# full specification

# raise .clicked(.container, EventArgs() )
# specifying a fake sender; full Specification EventArgs() explicit


The shortened forms cover the most common cases (?) and the full form is there if need to do something uncommon ( explicitly)

Re the call/parens style:
I agree that the already implemented keyword statements use comma sep lists is a really good reason not to do this.
( that plus I've spent most of today (its cold and rainy out) doing an implementation of this
- the keyword + comma sep list implemented easily and rapidly,
the parens/fn call style has not - lotsof weird little failure points sprinkled all over and ugly casting which leads me to believe I'm pushing against the flow)
I still think it reads better but can live with the alternative

Code: Select all
raise .clicked
# No this and EventArgs() - null EventHandler

# 1 arg = No this
raise .clicked, EventArgs()
# this assumed

raise .clicked, this, EventArgs()
# full specification

raise .clicked, .container, EventArgs()
# specifying a fake sender; EventArgs() explicit

raise .clicked, this
# Invalid sorta - this passed to args - becomes same as raise .clicked, this, this



I think in writing your own Observables theres usually 2 main Observer cases:
1) Only interested that something happened - pure event no state ( null Event Handler)
2) Want some indication of Event state - EventArgs subclassed, fields added for specific info ...
In any case when there is state being supplied I think it needs to be explicitly shown i.e never assumed - even if its just a non subclassed EventArgs
( in that case you'd still need an explicit EventArgs construct call to populate it with the specific values you want to pass on - If you're passing a
novalue EventArgs ( EventArgs() ) You're doing the equivalent of a no state event anyway...)
hopscc
 
Posts: 632
Location: New Plymouth, Taranaki, New Zealand

Re: Event declarations and firing Events

Postby Charles » Tue Jul 29, 2008 11:51 pm

My desire to imply EventArgs() instead of nil has to do with my aversion to nil references which lead to run-time exceptions. Also, if the event args class for that event signature has a default constructor then it's perfectly reasonable to use it. And if it doesn't then it's usually the case that you really need to explicitly create an instance. My experience in C# is that it would be rare to have a MyEventArgs(a, b) and not want to pass the "a" and "b" information along.

Also, for event signatures declared in Cobra, an argument such as "args as MyEventArgs" defaults to non-nil. You'd have to put a ? on it to accept nil.

Finally, I just checked in initial support for declaring and raising events. No support for default arguments yet. Example Implementation
Charles
 
Posts: 2515
Location: Los Angeles, CA

Re: Event declarations and firing Events

Postby hopscc » Wed Jul 30, 2008 6:07 am

I dont understand what you mean by 'imply EventArgs instead of nil' or where the nil discussion is coming from.
None of the 3 cases pass nil around.

The first case is for a Null (arg) EventHandler - a method that takes no args. When the event is fired it passes no args.
Its the degenerate case for Pure event notification (no state)
Code: Select all
sig NullEventHandler
...
event anEvent as NullEventHandler

def onAnEvent
  # this in cobra even tho it will be elided into gen c# code
   NullEventHandler h = .anEvent
   if h
        h()     # Null event handler invocation no args sent

...
    def getAnEvent
        print 'Got an event - no args'
...
    listen .anEvent , ref .getAnEvent


My other point was that if you are designing the event to pass state then it should have to be explicitly specified ( at the raising end)- not assumed and inserted by the compiler (even if its an empty/default constructor or a unaugmented EventArgs(). This way its always explicit and obvious on code reading what the state info to be sent is. The other is a rich source of obscurely wrong behaviour
hopscc
 
Posts: 632
Location: New Plymouth, Taranaki, New Zealand

Re: Event declarations and firing Events

Postby hopscc » Wed Jul 30, 2008 8:11 am

Coupla little buglets in that Example implementation
-emitted C# code for the Event doesnt actually have the 'event 'modifier.
-calls to raise on successive lines gives a parse error about 'expecting EOL and getting raise'
need a .undo after call commaSepExprs in raiseStmt

The raise implementation doesnt work for events declared with leading underscore names (protected)
was that intentional?
- recognise IdentifierExpr in RaiseStmt._bindImp, set DefaultAccessLevel to 'protected' if underscore name in BoxEvent
Attachments
raiseEx.patch
(1.84 KiB) Downloaded 546 times
hopscc
 
Posts: 632
Location: New Plymouth, Taranaki, New Zealand

Re: Event declarations and firing Events

Postby hopscc » Wed Jul 30, 2008 5:49 pm

This form of event raising is a race condition in c# in a multithread environment evidently
Code: Select all
if (event != null) { event(this, args);

handler added or removed between test and invocation
reccomended pattern is
Code: Select all
eventType hdlr = event;
if (hdlr != null)
  hdlr(this, args);


For cobra to do that needs to capture the delegate signature and emit in into event raising code.
Uploaded patch includes above plus mod for raise code emission plus protected event test case.
Attachments
raiseEx1.patch
(4.23 KiB) Downloaded 546 times
hopscc
 
Posts: 632
Location: New Plymouth, Taranaki, New Zealand

Re: Event declarations and firing Events

Postby Charles » Wed Jul 30, 2008 6:00 pm

I've read about that, but always wondered if it was really true. Doesn't the assignment just assign a reference to the event? In that case, the event, which is backed by a multicast delegate, is still running through the same list of targets which could be modified by their handlers.

Does your test case actually fail if the local var assignment is missing?
Charles
 
Posts: 2515
Location: Los Angeles, CA

Next

Return to Discussion

Who is online

Users browsing this forum: No registered users and 40 guests

cron