Replacing Events with AS3Signals
I’ve been playing with AS3Signals lately, and think I will be replacing flash events with it on all future projects.
It’s so simple!
So what is AS3Signals?
AS3Signals is a lightweight replacement for Flash Events written by Robert Penner.
AS3Signals aims to reduces the amount of traditional ‘boiler plate’ code required to receive and dispatch messages in Flash.
Why use AS3Signals?
Writing custom events tends to be a bit of a tedious chore on most projects, and I know many developers approach custom Events in different ways.
Some choose to create a single flexible Event with a loosely typed payload (ie. data:Object), while others like myself, like to strongly type events with value objects wherever I can (resulting in many custom event classes per project).
AS3Signals practically does away with the need for custom event classes altogether, as you now simply append any number of parameters to your signal.
The time saved not having to write all of these custom events alone is reason enough to jump into AS3Signals.
AS3Signals also brings a few nice features that events don’t have, such as “addOnce” that automatically removes the listener the first time a signal is received, “removeAll” which removes all listeners of a given signal, as well as “NativeSignal” which allows you to map traditional events (like MouseEvents) to signals. Finally there is DeluxeSignal which adds the ability to get the target object of a signal, and enables bubbling
So how do I use AS3Signals?
John Lindquist has a video tutorial that sold the idea and simplicity of AS3Signals to me, and I expect just about any other flash developer who watches it.
I’ve included my own basic demo and source code below to illustrate just how easy they are to use.
Sample
You can click on the face icon to “hit” the player. The SignalsDemo.as class listens to the hit, kill and click signals from the Player.as class and prints messages to the textfield.
SignalsDemo.as
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 | package com.pleribus.as3signalsdemo { import flash.events.MouseEvent; import flash.text.TextField; import flash.display.Sprite; /** * @author Clint Hannaford */ public class SignalsDemo extends Sprite { public var player:Player; public var textOutput:TextField; public function SignalsDemo() { addPlayer(); addTextField(); // add signal listeners player.playerHit.add(onPlayerHit); player.playerKilled.add(onPlayerKilled); player.click.add(onClickPlayer); } private function addPlayer():void{ player = new Player(); player.init(); player.x = 30; player.y = 30; player.buttonMode = true; addChild(player); } private function addTextField():void{ textOutput = new TextField(); textOutput.x = 10; textOutput.y = 100; textOutput.width = 350; textOutput.height = 200; textOutput.border = true; addChild(textOutput); } //Signal Handlers private function onClickPlayer(event:MouseEvent) : void { // Hit the player when clicked on player.hit(); } private function onPlayerKilled(message:String) : void { textOutput.appendText(message+"\n"); player.playerHit.remove(onPlayerHit); player.playerKilled.remove(onPlayerKilled); player.click.remove(onClickPlayer); this.removeChild(player); } private function onPlayerHit(message:String, health:Number) : void { textOutput.appendText(message+" - health now: "+health+"\n"); } } } |
Player.as
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | package com.pleribus.as3signalsdemo { import org.osflash.signals.natives.NativeSignal; import org.osflash.signals.Signal; import flash.display.Sprite; import flash.events.MouseEvent; /** * @author Clint Hannaford */ public class Player extends Sprite { public var playerKilled:Signal; public var playerHit:Signal; public var click : NativeSignal; private var health: Number; public function Player(){ } public function init() : void { health = 10; playerKilled = new Signal(); playerHit = new Signal(); click = new NativeSignal(this, MouseEvent.CLICK, MouseEvent); with (this.graphics){ beginFill(0x000000); drawRect(0, 0, 50, 50); endFill(); beginFill(0xFFFFFF); drawRect(10, 10, 10, 10); drawRect(30, 10, 10, 10); drawRect(10, 30, 30, 10); } } public function hit():void{ health--; playerHit.dispatch("Player Hit", health); if (health <= 0){ kill(); } } private function kill():void{ playerKilled.dispatch("Player Killed"); } } } |
Fantastic, you’ve sold me, I’m using it for a Flex AIR app i’m starting on now
I am thinking of writing an Hello world tutorial on Signals,but your example simply the best. Still,I will try and pen down my ideas to make me understand the Signals better.
thanks for the example
saumya
Simple, clear, .. excellent!
I’ve played around with Signals, and honestly I feel that Events are still easier and better. For instance, you say that you now feel “freed” from the overhead of writing all of those custom events. But you’ve also given up what those custon typed events gave you–type safety. Because Signals relies on passing in references to Functions, which provide no information at runtime on what type of arguments they need, the first hint you have that you (or someone) have made a mistake in using a Signal is a runtime error.
Also, support for bubbling Events is built into the Flash Player. While Signals offer a sort of approximation of bubbling, it’s dependent on each object’s having special logic to be able to continue the bubbling. With Events, you can dispatch an Event from a button inside a MC inside another MC, and only the button and whatever is listening need to know anything about the process.
Additionally, an Event is an Object that you can save for later use (think Redo after an Undo), and it retains all of its properties so you don’t need to care what specific type of event it was or know anything about the conditions in the application when the Event was first created.
Using Signals also limits reuse–it creates a dependency on the As3Signals library that someone using your code might not care to have.
Given how much you give up for the neato-coolness that is Signals, I’ve seldom found it worth the tradeoff for more than the simplest tasks.
Hi Amy,
Thanks for the thoughtful response. I agree with you that Signals provide some of their own problems, but in the long run, I’ve found them to be very useful in reducing the sheer number of custom Event classes that we tend to generate in a project (usually larger projects). Particularly when using our preferred combination of RobotLegs and GaiaFramework (or custom front end framework).
We separate the RobotLegs and Gaia layers through interfaces, and pass ui events between the 2 for communication.
We find that using Events in this situation, we tend to balloon in the number of basic UI Events as the project grew, to the point of it getting ridiculous. The time it takes to write, maintain, and even locate these event classes in a large project towards the end becomes a frustration. Each developer brings their own nuances to the way they write and use custom event classes (probably worth a whole other post), and where they place them in a projects structure as well, which can be fun to maintain in tight deadline situations.
Signals eliminates this, reducing the complexity and number of classes required.
You do give up the benefits you mention, type safety and only getting run time errors, etc. At the end of the day, I guess it’s a personal choice.
Bubbling is also a bit of a non issue for me, as I rarely require it and tend to avoid where I can. It can make some code very difficult to follow when debugging, and a class you hadn’t suspected is receiving a rouge event you weren’t expecting.
It may seem like the latest neato-cool thing to do, but we have found some real benefits in using Signals.
@Amy, your comments are intriguing and leave me with questions.
“you’ve also given up what those custom typed events gave you–type safety.”
How exactly do custom Events give you more type safety than Signals? One of my main goals with AS3 Signals was to *increase* type safety. Perhaps I need to communicate better how that happens.
“Because Signals relies on passing in references to Functions, which provide no information at runtime on what type of arguments they need, the first hint you have that you (or someone) have made a mistake in using a Signal is a runtime error.”
How is this different from Events, which also use function listeners?
What compile-time errors protect you from using Events incorrectly?
If you’ve never made the following mistake, you’re luckier or more observant than I:
loader.addEventListener(Event.COMPLETE, onComplete);
This looks good on first glance, but onComplete() will never be called. Does the compiler help you here? Not at all–this code compiles just fine.
Suppose instead that Loader used Signals, or something like it. This would be the equivalent code:
loader.complete.add(onComplete);
This would never compile because Loader wouldn’t have a “complete” property. The compiler error would quickly force the user to go back to the docs and learn about contentLoaderInfo. But with Events, even I have wasted time puzzling over code not firing after a load.
@Amy, with respect to bubbling:
“While Signals offer a sort of approximation of bubbling, it’s dependent on each object’s having special logic to be able to continue the bubbling.”
This is a misunderstanding which I will chalk up to inadequate documentation. DeluxeSignal.dispatch() will bubble an IEvent up the chain target.parent.parent… as far as it can. So DisplayObjects will automatically be bubbled through.
Here’s the relevant DeluxeSignal code:
https://github.com/robertpenner/as3-signals/blob/273f5a1f080e00660c9cfeb1384837a0e496c990/src/org/osflash/signals/DeluxeSignal.as#L139
The mechanism of handling the event and then stopping or continuing the bubbling from there is what is custom to DeluxeSignal. I tried to create something more flexible than Events, which can only bubble through DisplayObjects.
But there’s always NativeSignal, which integrates standard Event bubbling with a nice Signals veneer.
@Amy,
“Additionally, an Event is an Object that you can save for later use (think Redo after an Undo), and it retains all of its properties so you don’t need to care what specific type of event it was or know anything about the conditions in the application when the Event was first created.”
I’m not sure what you’re getting at here. Is this something you think is lacking with Signals? You’re free to use a Signal to dispatch anything from an Event to a Boolean to nothing at all.
Thanks for the response Robert, always a pleasure to see you responding in person to discussions about Signals.
I honestly wasn’t sure how strongly typed Signal variables were.
Thanks for the insight.
I do understand Amy’s plight with making the jump from Events to Signals, it took me a little time to really feel comfortable with them, but as I state, I’ve come to really find them beneficial.
I particularly find them most useful in the View/Mediator relationship, where we cut out all the unnecessary custom UI events, for the lighter weight Signal. Especially in situations where we end up with almost identical events, with slightly different payloads due to a given condition of the system (which results in ballooning of event classes).
It does take people time to change their stance on Signals vs Events, and even then it can be personal preference.
The developers I’ve worked with all like what Signals brings to a large team project.
@Amy,
“Using Signals also limits reuse–it creates a dependency on the As3Signals library that someone using your code might not care to have.”
True enough. But I could just as well say:
“Using JSON limits reuse–it creates a dependency on the as3corelib/actionjson/other library that someone using your code might not care to have.”
Flash Player has great XML support built right in–why use anything else? =)
Here’s another example of how AS3 Signals provides more type safety than Events.
If you dispatch the wrong class of Event, you will not get an error unless you’ve added a listener with the correct Event signature.
// This will NOT cause an error, even though it’s wrong.
var sprite:Sprite = new Sprite();
var clickEvent:Event = new Event(MouseEvent.CLICK);
sprite.dispatchEvent(clickEvent);
// The listener throws type coercion error on dispatch:
// cannot convert Event to MouseEvent.
var sprite:Sprite = new Sprite();
sprite.addEventListener(MouseEvent.CLICK, function(e:MouseEvent):void {} );
var clickEvent:Event = new Event(MouseEvent.CLICK);
sprite.dispatchEvent(clickEvent);
With Signals, however, dispatch() actually makes sure you’re sending the correct type, even if there are no listeners.
// This WILL throw an error.
var sprite:SignalSprite = new SignalSprite();
var clickEvent:Event = new Event(MouseEvent.CLICK);
sprite.click.dispatch(clickEvent);
The dispatch() type checking brings this mistake to the developer’s attention faster than Events does.
@Rob, @Clint,
I think we’re looking at it from opposite sides of the equation. I see it as far more important for the listening function to know what it’s getting than for the sending function to send the right thing. I think we listen far more than we send (in terms of number of places we write code).
And Signals has no way for me to be sure that the types on the arguments of my _receiving_ function are the same as what are being dispatched. Worse, there is no code hinting. So, to know how to set up my “listener” function, I pretty much need to be looking right at the code or docs for the Signal itself.
The ability to know _exactly_ what type of “thing” you’re _receiving_ function is the vast majority of what you gain from writing typed events–and it’s something you completely lose using Signals. It’s great that you do all kinds of checking on the send side, but because you’re using functional programming, you’ve stepped into a world where the compiler and the IDE won’t help you on the receive side.
You could argue that you can go in and create templates in an IDE tool that supports it, but what advantage does this gain over using the same technology to stub in your method body? Also, if you’re writing so many custom events that you think it reaches the point of the ridiculous, how is it that it’s not now automatic? Really, a custom Event takes about 2 minutes.
About bubbling: Typically I use bubbling inside a Mediated view. The Mediator knows that “something” inside the View will dispatch that event, but it doesn’t need to care where inside the heirarchy it originated. These events never have any payload at all, so they don’t use a custom Event class at all. Using this architecture, though, I can rearrange that View without regard to the effect on the Mediator, or I can move the View and put it somewhere else, and the new place where it is used can do what it wants with those events–whether that new destination happens to be using the same Libraries or not.
If my Views need to do something more complicated than that, I move to Presentation Model, and the PM will handle the details of creating and dispatching the custom Events (into the Event Bus, which is the only place where I use custom events).
@Rob, about the fact that Signals can send anything, yes, that is true. However, if you’re going to write a custom Object that has the information needed to reconstruct a system state that isn’t current, why not just use a custom Event that you can just dispatch to the bus to cause the change to the Model and then store for later use?
So I guess the bottom line is that I see Events primarily as an Object that carries a lot of information to be used to determine what to do, and I find that Object to be extremely valuable in and of itself. Yes, I could use Signals to pass those around, or some other Object that does the same thing, but why, since the Flash Player does a good job already?
Every library you add adds more overhead, and you just have to make decisions as to whether what you gain by using them is enough to make it worth it. And so far, my own answer has been no–especially on my current project, where the ability to store Events for later use is core to how it works.
@Rob: Also, I see the fact that you can listen for an event that might never be dispatched as an _advantage_ of Events, not a disadvantage. If you want things to stop happening that happen as a result of a “closeMe” Event, you simply stop dispatching that Event. By the same token, you can manage several Views that may or may not dispatch “closeMe” Events with the same logic–those that happen to dispatch “closeMe” will trigger the closeMe listeners, and those that don’t, won’t.
And, really, there is nothing that says that a Class that declares a closeMe Signal will ever dispatch it–just that it _may_, which is exactly the same guarantee you get from Events.
@Amy In regards to code hinting, you are pretty much referencing the actual signal when you write your add() function. A quick F4 ‘follow’ takes you to the signal where you can see what it dispatches.
If you’re using generic events, and not custom ones, you have the same code hinting issue.
ie this.addEventListener(“some_custom_string_another_developer_may_have written_that_I_have_to_look_up”, handleThisEvent);
As for the multitude of Custom Events, you are right, we could have used generic events in most cases (with the same approach as the code above), but you still have to go and search for the dispatched event to see what it’s called.
We originally used Custom ones for this very reason, as well as the need for the occasional payload, so notch one up for loss of code hinting. But, it hasn’t really impacted my efficiency a great deal using signals.
Also, I don’t think it was so much the time it takes to write the custom event (which is minimal), it’s moreso the managing of them towards the end of projects (generally those that are large with multiple developers and with tight deadlines we often have in the advertising space).
Using the correct ones (ie cases of wrong event which may have sounded similar), avoiding conflicts and confusion of their use with other developers on the team, finding a folder of events buried in a developers own component of work etc, etc.
Anyway, it’s a matter of personal taste I think. I really like how clean Signals has made our work (especially with our chosen frameworks). I like that we don’t have event folders all over the shop with a multitude of events with varying implementations from different developers.
And it’s not that we have stopped using Events. We obviously run into them all the time, with TweenMax, Gaia, etc. We just try and use Signals in our core code where we can.
To each their own…
@Amy, first off, thanks for the discussion. I’ll be clear that my objective isn’t to convince everyone to use AS3 Signals all the time. Rather, it’s to debate open questions about their merits relative to alternatives such as Events.
“Signals has no way for me to be sure that the types on the arguments of my _receiving_ function are the same as what are being dispatched.”
I wouldn’t say “no way.” If that were the case, developers would get frustrated and stop using Signals. Here are three ways people can, and do, have confidence in their listener signatures:
- documentation
- source code
- the code works without throwing a Type Coercion failure
Could it be better? Yes, if ActionScript were improved with generics and stronger function types.
Let me throw this back at Events: How can you be sure your listener Event argument is correct? The compiler certainly doesn’t help you. It takes extra metadata and convention following to achieve this. Personally, with Events I find myself bouncing around asdocs a lot tracking down which constants to use.
“Worse, there is no code hinting.”
The name of the Signal is code hinted, enabling the compiler to guarantee you’re listening to something valid. This is something Events cannot do. However, the Signal signature is not code hinted, due to limitations of ActionScript.
Events only support code hinting when [Event] metadata is used AND kept in sync with the relevant ActionScript AND a specific convention with Event constants is followed. If any of these 3 three stringy links is broken, the chain fails.
AS3 Signals could use metadata to achieve the same level of code hinting as Events. As of yet, though, it’s not something the community has demanded. We seem to manage fine without adding extra stuff that has to be maintained and kept in sync with the real code.
Amy said: “… really, there is nothing that says that a Class that declares a closeMe Signal will ever dispatch it–just that it _may_, which is exactly the same guarantee you get from Events.”
Amy – that misses the point. There is a clear conceptual difference between ‘not dispatching’, because the implementation has blocked it, and ‘never relevant’.
I can, of course, choose to have my signal implementation not actually dispatch a particular signal – just as with events – but I can’t choose to listen for a signal which has no relevance to that object.
If I want to, I can listen for a MouseEvent.CLICK on any IEventDispatcher – including a model which has no presence on the display list. I can listen for Video events on an XML loading service. This is broken.
As Rob says, it means when I add a listener to a custom object I often have to start referring to the AS3 reference to check exactly what event is dispatched when by this object.
Even with event hinting, I get very little information to help me figure out – for example – whether there is a progress event dispatched after complete, always immediately before it, or only sometimes immediately before it.
With signals I can create beautifully expressive getters that make this obvious, with very little extra overhead:
progressUpdateBeforeComplete
progressUpdateAfterComplete
(I realise that I could do the same by having multiple event strings, but much less elegantly for the user of the API).
However, my own favourite thing about Signals is using them in composite views. Let’s say our view is a login panel with 3 buttons – ‘submit’, ‘quit’ and ‘forgot password’. With events, I either have to create and dispatch a different event for each click, or I have to expose those buttons (which opens up all their properties: position, enabled, alpha yada yada) to something that wants to listen for the clicks on them. Or I can bubble the event and do a switch on the target – and again this target means that I have access to set any property of that button.
With signals I can just add 3 simple signals to the parent view – one for each click.
In my opinion, the signals approach is closer to the kind of ‘need to know’ basis that I want my code to work on. Does the listener need to move these buttons around? Does it need to shift them in position? No – it just needs to know when they’ve been clicked. *If* it wants the buttons to move or change alpha then it should go through an API on the parent.
Personally I still use a mix of events and signals. I’ve done a lot more than ‘play around with’ Signals – I use them in our production code all the time. They are incredibly useful – for example – for implementing a type-safe-at-runtime (ie easy to debug) request-response pattern allowing a specific instance of a class to receive an async update on its own schedule, without having to do horrible hacky workarounds such as arbitrary IDs on events. (Approaches such as only adding the response handler locally don’t work because you’re then relying on order-of-executions and assuming only one request at a time).
I’m intrigued that you’d be so invested in slating an approach that so many people are finding to be useful. Putting all the implementation details aside, there are 3 things we know for sure about signals:
1) They’re the product of an enormous amount of energy invested by numerous very smart people over a long period of time. (Robert hasn’t just been locked in his attic working on a secret master plan).
2) The process of developing them has been done in the open with lots of healthy discussion, debate, testing, heuristic feedback, revision, refactoring, branching (to consider the merits of alternative approaches) and intentional reflection by the community.
3) They offer a different approach to achieving loose coupling between objects, with solutions for some of the problems that have irked many programmers about the AS3 events system.
What’s to object to? Provided A and B are both of high quality, having a choice between A and B is a useful thing IMO. Mindful choices allow us to be more intentional in our architecture and implementation. Even if neither A nor B is perfect, having alternatives with different strengths and weaknesses allows us to optimise.
I’m going to start with @Stray, because she and I seem to enjoy having spirited debates on a regular basis .
“There is a clear conceptual difference between ‘not dispatching’, because the implementation has blocked it, and ‘never relevant’.”
The thing is, that if suddenly you decide that it’s not relevant anymore, you can’t remove the Signal without breaking whatever is listening for it–because it requres that you directly reference the Signal itself. I know that things like the SignalCommandMap somewhat alleviate this, but I’m talking about in normal use.
If you remove code that dispatches an event, all the code that was intended to trigger when it fires is still relevant and could be called upon if you change your program again.
“As Rob says, it means when I add a listener to a custom object I often have to start referring to the AS3 reference to check exactly what event is dispatched when by this object.”
…and you have to look at the Signal to see what data types will appear in the arguments list. I think that’s pretty much a wash.
“(I realise that I could do the same by having multiple event strings, but much less elegantly for the user of the API).”
Some users find needing an additional Library inelegant compared to using constants that the IDE will mostly type for them anyway.
“With events, I either have to create and dispatch a different event for each click, or I have to expose those buttons (which opens up all their properties: position, enabled, alpha yada yada) to something that wants to listen for the clicks on them. Or I can bubble the event and do a switch on the target – and again this target means that I have access to set any property of that button.”
Or you can use PresentationModel, and allow the PM to determine the meaning of the gesture (which is roughly equivalent to what you’re doing in Signals, but carries some other functionality as well that you might find advantagous or not).
“(Approaches such as only adding the response handler locally don’t work because you’re then relying on order-of-executions and assuming only one request at a time).”
You might want to look at the way AsyncToken works in Flex and see if that isn’t a better way to handle it.
“I’m intrigued that you’d be so invested in slating an approach that so many people are finding to be useful.”
That’s kind of a harsh interpretation of what I consider a simple discussion of the pros and cons of a specifc technology. But the reason I’m willing to look at the other side of this is the same reason I’m willing to reevaluate the truism that anonymous functions are always bad or any of those other things we’re asked to believe are true.
“Mindful choices allow us to be more intentional in our architecture and implementation.”
Precisely. And for what I’m doing now, at least, the other techniques I am using serve me every bit as well as using As3Signals, without introducing yet another library. I would encourage others to be very thorough in their evaluation of what they get with As3Signals vs. what they lose.
As Rob says, the way functions are treated in AS creates some fundamental weaknesses whenever you use functional techniques, and these are at the core of the problems I have with As3Signals. Personally, I would have preferred that Adobe move addressing these above trying to add multithreading, but they didn’t AFAIK.
@Rob
“First off, thanks for the discussion. I’ll be clear that my objective isn’t to convince everyone to use AS3 Signals all the time.”
You too . I actually have a very strong interest in Signals, especially if NativeSignal can do better filtering on native Events (not call every listener and then make them filter on event properties). I plan to get there eventually, but I keep getting sidetracked on things that won’t take me 2 solid days to do.
“I wouldn’t say “no way (to know the types of arguments that will be dispatched).” If that were the case, developers would get frustrated and stop using Signals. Here are three ways people can, and do, have confidence in their listener signatures:
- documentation
- source code
- the code works without throwing a Type Coercion failure”
It looks like somewhere in there we lost track of the fact that I was originally talking about code hinting help from the IDE–any time you have to go look something else or, worse, run the code to find out I’ve made a mistake is going to slow me down. Maybe I would fare better if I weren’t using Flash Builder on a Mac these days, but I don’t find that it makes efficient use of the screen real estate I have easy.
“Events only support code hinting when [Event] metadata is used AND kept in sync with the relevant ActionScript AND a specific convention with Event constants is followed. If any of these 3 three stringy links is broken, the chain fails.”
I think we’re talking about two different things here. If I receive an object of type FontStylingEvent, code hinting will, indeed, tell me it has a fontStyling property.
I usually don’t care about the kind of code hinting you’re talking about, because my events will either be sytem level events (dispatched against the central event bus by a Mediator, Command, or Presentation Model), or they’ll be so extremely local that there’s no issue taking a more direct route (looking at the code) to seeing what events are being dispatched.
When events are dispatched against the central event bus, they nearly always have a payload and thus will have a type constant defined by the custom event. I tend not to write custom events for events that do not have a payload (which are few), preferring instead to have a Class that simply defines the constants.
I agree with everyone in this discussion that Signals is a great idea and that its weaknesses are primarily due to failures in As3. However, those weaknesses do exist, and right now I don’t find the argument for Signals compelling enough to incorporate it, though I have in a previous project with different goals and limitations.
“The thing is, that if suddenly you decide that it’s not relevant anymore, you can’t remove the Signal without breaking whatever is listening for it–because it requres that you directly reference the Signal itself.”
Yes. Correct. I interpret this as a benefit of Signals – a breaking change in terms of app behaviour is also a breaking change to the compiler. No silent just-didnt-do-what-it-was-supposed-to bugs.
“I tend not to write custom events for events that do not have a payload (which are few), preferring instead to have a Class that simply defines the constants.”
You mean you actually prefer strings to strong typing?
@Amy,
“If I receive an object of type FontStylingEvent, code hinting will, indeed, tell me it has a fontStyling property.”
Is that really all you’re talking about re: code hinting? In this case, Events don’t have an advantage over Signals, which can send FontStylingEvent or anything else.
I’d like to repeat my earlier question you haven’t answered:
How exactly do custom Events give you more type safety than Signals?
You said “[With Signals] you’ve also given up what those custom typed events gave you–type safety”. I gave several examples of how Signals improve type safety over Events. Do none of these matter to you? Is there still some pocket of functionality where you think Events have more type safety than Signals?
Type safety was the first concern you brought up in your critique of Signals. At this point, I’d consider that debunked.
@Amy,
“It looks like somewhere in there we lost track of the fact that I was originally talking about code hinting help from the IDE”
Ok, help me not to lose track. Do you have other examples besides FontStylingEvent? Examples where Events allow better IDE code hinting than Signals?
@Amy,
“As Rob says, the way functions are treated in AS creates some fundamental weaknesses whenever you use functional techniques, and these are at the core of the problems I have with As3Signals.”
How exactly do functions create problems for AS3 Signals but NOT for Events?
Listeners for Events and Signals are basically the same. The main difference is that Signals doesn’t force a certain signature (event:Event) on you. If listener references that lose type safety are bad for Signals, they’re also bad for Events.
Are you able to explain your core problems with Signals any further? So far I haven’t seen anything from you re: AS functions that applies only to Signals.
I debated long and hard with myself about whether to come back to this discussion. As soon as someone (even partially) declares “the debate is over and I won!” I find it hard to believe that he has a sincere interest in my point of view. However, there are others following this, so I thought I’d try one more time.
My husband, @SteveHoward999, says I often assume what is obvious to me is obvious to everyone, so let me go back and spell out in so many words what I believed at the outset should have been obvious, just in case it wasn’t.
Clint, in his original post, says: “others like myself, like to strongly type events with value objects wherever I can (resulting in many custom event classes per project).” I presumed, rightly or wrongly, that this means that he found value in these strongly typed Events–specifically, in their Value Objects. This seams to me to be a reasonable reading of the sentence.
From there I said that this strong typing is lost when you use Signals. Again, it is very abundantly obvious to me, and several people have confirmed, that Signals CANNOT check that the arguments on listening functions are of the correct type, and certainly not at compile time. This is simply due to the way that As3 handles function references.
By contrast, you CAN’T use dispatchEvent() to dispatch, say, a DisplayObject. So, at the very least, you know that you’re getting an Event. I suppose that some developers have trouble constantly dispatching or listening for the wrong Event Class but, in practice, I have never encountered this problem–and I have seen a lot of truly atrocious code.
The simple signatures for both dispatching and receiving events make _that_ part of the process pretty idiot proof. And the compiler will alert you if you try to add an event listener that doesn’t have at least one argument–something that you’ll only get warning of at runtime with Signals.
Several people have talked about the type _property_ of an Event Object as somehow being involved in its type safety. This property is, in fact, a strongly typed Object (a String), but it doesn’t matter whether you are using a strongly typed event, a regular Flash Event, or a DynamicEvent with a data property, this property always has exactly the same type safety. You can help make sure the same String is used throughout the application by using Constants, but whether those constants are used as part of the Event Class definition or not (or whether you don’t even use constants) has zero bearing on the type safety or lack therof of the property.
So, it was very clear to me that this was _not_ what I was saying about the type safety you throw away by not using Events. I was actually pretty surprised to be asked to defend a position I didn’t take on this.
I will say this: I think that the “type” property has a very clear semantic meaning, which is “this is the specific way I want this (Class of) Event dealt with.” The Flash Player has traditionally dealt with this by routing all Events with the same type property to the same handler, without regard for the actual Class of the event. However, the way that Robotlegs handles this, by looking at both “type” and type, proves that the semantics of the default Event object are quite workable. If the FP implementation is a bit flawed, it still works extremely well.
Now let’s look at the suggestion of using Signals to send an Event directly to a function. The Signal itself coopts and repeats the meaning of the Event’s type property, and this property (the only mandatory Event property) could well become meaningless. While this might have value in retrofitting Event-based code to use Signals, in new code this has a bad smell IMO.
I’m going to move on to talking about my philosophy toward loose coupling, which is to use the absolutely least prescriptive Class Type needed to accomplish the job. If I have, say, a LoginView that has a SimpleButton that dispatches a bubbling LoginEvent, I can listen for that event on a LoginView instance without knowing anything about it other than it is a DisplayObject. If I needed to take it that far, I could type it as IEventDispatcher.
However, to listen for a Signal to be dispatched, I _have_ to know about a Signal instance, so if we have a bubbling child Signal, at the point we’re listening to it the parent needs to implement IBubbleEventHandler and have a matching Signal itself. This means that (in normal usage), we need to know something fairly custom about the Class involved here.
In order to dispatch our LoginEvent from our SimpleButton, we don’t have to do anything but call the dispatchEvent function that it already has. And–and–LoginView doesn’t need to have any knowledge that something has happened.
To dispatch the bubbling Signal, you need to add the Signal and call its dispatch() method in the child view, then catch it and redispatch it in the parent.
From a loose coupling point of view, this all doesn’t sound like better architecture. It’s not that you can’t make it work, but I don’t find the thing you see as so advantageous–the necessity for direct knowledge of a property on the object you want to listen to (and for parents to be so concerned about what is going on inside them)–to be a good thing in a general sense.
Anyway, like I said, I felt I owed it to those following the discussion to come back and clarify the points I may not have been very clear on before. If I have failed this time, I apologise. This was my best shot, and I don’t see much point in continuing to discuss if it didn’t work.
Apologies Amy, I completely missed your comment arriving in my inbox. Just approved it.