Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings
This repository was archived by the owner on Aug 31, 2021. It is now read-only.

🍄 docs: Add specification for LCB widget signals and events. #3495

Open
wants to merge 4 commits into
base: develop
Choose a base branch
Loading
from

Conversation

peter-b
Copy link
Contributor

@peter-b peter-b commented Jan 21, 2016

Initial draft of specification for revised LiveCode Builder signal and event subsystem.

Rendered version

Unresolved issues:

  • What happens when an event handler blocks/waits in the "dispatch" phase (possibly indefinitely)?
  • Relationship with LCS before and after handlers
  • More / better examples

@peter-b
Copy link
Contributor Author

peter-b commented Jan 21, 2016

@trevordevore @montegoulding @livecodeali @livecodefraser @runrevmark

I'd appreciate some prompt feedback, please, since getting this sorted is blocking LiveCode 8 RC 1.

* Signal handling order is arbitrary
* `the event target` - and suggest `the event target is me`
* Note on implications of `capture event` changing
  `the event target`
Signal handlers are explicitly declared in an LCB program using a `signal handler` declaration [1]:

signal handler for <name:id>(<params:formals>) is <handler:constexpr>

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@peter-b As with event handlers it seems to me that it is unlikely that you would declare multiple handlers for a signal or event so would it be simpler to just replace public with signal or on event in the handler?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@montegoulding There are three reasons that a separate declaration is necessary:

  • Handling multiple signals with distinct names but the same formals with a single handler
  • Handling multiple signals with the same name but different formals (LCB handlers will never be able to be overloaded)
  • Avoiding ambiguity when a widget can both emit and handle a signal with the same name, without enforcing specific handler name mangling.

I will add more notes tomorrow!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@peter-b Regarding point 2 is this to deal with multiple child widgets that may emit the same signal name? One thing is it could be feasible for different child widgets to emit the same name with the same formals but for logically different reasons or at least the parent widget author may want to handle them separately. Perhaps the <name:id> should/could include the widget kind? Maybe a nice thing to do would be something like:

signal handler for [<widget kind>|*]:<name:id>(...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Technically you cannot have two signals with the same name and different formals. Signals are declared in a module, so their name is actually 'module_name.short_signal_name'. The idea is that if an identifier for a definition are imported from two different modules then the identifier becomes ambiguous, and you need to use the full name for it in all places. (Note that lc-compile doesn't currently implement this...).

In regards to the automatic attachment then there is more we can do there. At the moment attachment is hard-coded based on name, but you create the child widgets in code. Ideally, there'd either be a means to define the child widgets as definitions in the module (if your widget always uses a fixed set) which would mean you wouldn't need to write code to do it; or a means to dynamically connect signals of just created widgets to handlers in the current module.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See my top-level comment about connecting to and emitting uniquely specific signals. If we implement it that way, then (a) it's easy to reason about (just use the existing definition lookup code and (b) there's no problem with child widgets defining differently incompatible versions of the same named signal.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Heh - I think (having skimmed the comment earlier and then forgotten about it!) had your further suggestion implicitly in mind when I wrote the above... Yes - the improvement @peter-b suggested combined with the proposed name conflict semantics solve the intent and signature problems.

Sent from my iPhone

On 22 Jan 2016, at 21:25, Peter TB Brett notifications@github.com wrote:

In docs/specs/lcb-signals-and-events.md:

+1. These declarations provide a convenient anchor point for documentation comments about the purpose and usage of a signal.
+
+2. In principle a widget could set properties on itself before emitting a signal. However, when adding features to LCS previously it's been found that users prefer relevant information to be passed as parameters to messages rather than out-of-band. Passing parameters to signal emission also helps to ensure that all of the correct setup has been performed for signal handling - for example, if a parameter is added or removed from the signal, it's easy to verify that all relevant code has been updated.
+
+3. Although, initially, signal emission could be implemented by iterating over registered signal handlers and calling them within the execution of the emit statement, this could easily lead to arbitrarily-deep signal emission chains going backwards and forwards between LCS and LCB contexts. By allowing signal emission to be delayed, this is avoided (the current stack can be allowed to unwind before signal dispatch is performed). Delaying signal handling also helps to reason about the safety of deleting a widget in response to receiving a signal from it.
+
+4. Emitting a signal twice from the same handler - even with the same parameters - will cause all of the handlers for that signal to be run twice.
+
+5. This is a consequence of asynchronous signal handling.
+
+### Signal handling
+
+Signal handlers are explicitly declared in an LCB program using a signal handler declaration [1]:
+

  • signal handler for name:id(params:formals) is handler:constexpr

See my top-level comment about connecting to and emitting uniquely specific signals. If we implement it that way, then (a) it's easy to reason about (just use the existing definition lookup code and (b) there's no problem with child widgets defining differently incompatible versions of the same named signal.


Reply to this email directly or view it on GitHub.

@peter-b
Copy link
Contributor Author

peter-b commented Jan 22, 2016

@montegoulding @trevordevore I've been thinking about the possibility (and was discussing with @livecodeali earlier today) the possibility of simplifying the events & signals stuff a bit more.

At the moment, we’re assuming that signal and event handling is based on a textual match between handler name and signal name. However, there are going to be a whole host of “standard” signals that widgets are going to want to reuse, such as clicked and selectionChanged

If we make signals and events first-class types, then it could simplify everything quite a lot.

We can create a library that provides definitions for “standard” signals:

module com.livecode.signals
public signal type Clicked()
…
end module

Widgets that want to emit the signal can then emit it by name in almost exactly the same way:

widget org.example.SignallingWidget
use com.livecode.signals
private handler synthesizeClick()
   emit signal Clicked()
end handler
end widget

And widgets that need to handle the signal can do so in an unambiguous and type-checked fashion, knowing that they’re handling the specific signal that they want to:

widget org.example.ClickReporter
use com.livecode.signals
signal handler for Clicked is ConsumeClick
private handler ConsumeClick()
   log "%@ was clicked!" with [the signal source]
end handler
end widget

Finally, having a standard library of well-known signals provides a useful place to document best practice, and lets composed widget authors more easily know how child widgets are expected to behave.

And so on for events.

@trevordevore
Copy link
Contributor

@peter-b I like this improvement. The lesson work that has to be done for each individual widget the better.


**Notes**:

1. These declarations provide a convenient anchor point for documentation comments about the purpose and usage of a signal.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@peter-b Have you considered how the use of a common module to declare signals will impact on documentation. It would be handy if things need only be documented once but if the module is one big blob of signals we perhaps need a way to tell the documentation generator which ones we are emitting? Could it be intelligent enough to parse our emit statements? Along similar lines of thought people need to know what events can be handled in the host widget script. Could the documentation generator determine what events are aren't captured and unpassed by the widget and its children.

* Make signal and event declarations into `type` definitions

* Suggest providing modules for "standard" signals & events

* Clarify why it's useful to have explicit handler declarations

* Explain that LCS event and signal handlers connect by name

* Remove `the event` syntax; just recommend that syntax for event-
  related data refers to the data of the most recently-processed
  event

* Clarify behaviour of LCS `the target` syntax

* Defined behaviour w.r.t. blocking operations
@peter-b
Copy link
Contributor Author

peter-b commented Jan 25, 2016

@trevordevore @montegoulding @runrevmark Here's a new revision, which addresses many of the issues mentioned above. In particular, behaviour w.r.t. blocking operations is now well-defined, and signals and events are now type definitions.

A post-event handler is declared using a `post event handler` declaration:

post event handler for <name:id> is <handler:constexpr>

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My comment about continuing the same model through to LCS and using before and after handlers have been lost. Should I write it out again or was it a silly idea. I might also suggest changing pre and post here to before and after mainly because post has multiple meanings.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It wasn't lost!

Unfortunately, and as you quite rightly pointed out in a previous comment, the before and after handlers in LCS work have quite a different semantic that relates to behaviours and not really specifically to events. @runrevmark and I had a long discussion about whether it was possible to rejig the way they work so that they can be used to do "pre" and "post" event handling. Unfortunately, we couldn't find a way to make that sane, so for the time being I think the plan is to make "pre" and "post" event handling available only in LCB.

This also gives the opportunity to check that the event model makes sense, since we are reserving the right to make backwards-incompatible changes to LCB in the future (but we don't have that luxury for LCS).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense. I'm looking forward to using the new pre/post approach. I like the idea of the outside-in/inside-out path for the events.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's tricky. In theory there are ways to do things in LCS that result in getting messages in a weird order. Imagine that you have a button with the following script:

on mouseEnter
   wait 10
   pass mouseEnter
end mouseEnter

on mouseLeave
   pass mouseLeave
end mouseLeave

On a card with the following script:

on mouseEnter
   put "mouseEnter" && the target & return after msg
end mouseEnter

on mouseLeave
   put "mouseLeave" && the target & return after msg
end mouseLeave
  1. What would you expect the message box to contain when you move the mouse pointer over and away from the button?
  2. What is the actual output that you get?

Most of this spec is designed to make sure that, for LCB widgets, expectation and reality match!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@peter-b I see what you mean about the event order.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In your original question you asked when the post-event should be sent if the LCS mouseUp event has a wait. My understanding of the question is that the post-event in LCB can be triggered after the LCS mouseUp event finishes (which would mean waiting until wait is done) or the post-event in LCB can be triggered in the same engine cycle that produced the mouseUp event. In the later case, the post-event in LCB would trigger before the LCS mouseUp event finished. In my mind the messages are then unbalanced because LCB is finishing before LCS does.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, but here's the problem -- the LCB widget implementation then has to be able to deal with, for two mouse clicks, the corresponding MouseDown, MouseUp, MouseDown, MouseUp post-event handlers being run in any order or possibly not at all (because nothing says that a LCS handler actually has to complete). Not to mention the fact that it is then impossible to ensure that only one event is in-flight at any given time. I'm not convinced that that behaviour is either sane or feasible to implement.

This is a discussion that's been going round-and-round-and-round-and-round between me and @runrevmark for some time now... Both options are basically terrible.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@peter-b If it is impossible to ensure that only one event is in-flight at any given time then you know it isn't a viable solution. Problem solved :-) So the sane solution is to have LCB event always sent within the same cycle the message was generated, right? That is straight forward.

So what types of problems does this lead to? LCS and LCB can now become out of sync with each other when it comes to event message order. The widget developer has no control over this as it is the developer using the widget that can cause this. The developer creates out of sync state by doing anything the blocks (wait, db operations, etc.) during one of these events.

Does that sum up the issue?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@trevordevore The current version of the spec would ensure that, if you block in an LCS handler, the LCB post-event handlers would be dispatched immediately and then normal LCS message handling would resume. This would mean that the widget's "leave"-phase handlers would be run while LCS is still technically in the "dispatch" phase.

I think it's a reasonable compromise. The LCS developer who blocks in an event handler would have to take responsibility for any ensuing out-of-sync state -- but that doesn't introduce any problems that aren't already present in LCS.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that it is a reasonable compromise. Carry on.

@peter-b peter-b removed this from the 8.0.0-rc-1 milestone Feb 13, 2016
@peter-b peter-b changed the title docs: Add specification for LCB widget signals and events. [DEAD]docs: Add specification for LCB widget signals and events. Apr 13, 2017
@peter-b peter-b changed the title [DEAD]docs: Add specification for LCB widget signals and events. 🍄 docs: Add specification for LCB widget signals and events. Apr 13, 2017
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants
Morty Proxy This is a proxified and sanitized view of the page, visit original site.