Natural Event Syntax

From BradyBunchGridDiscussion:

[It's perhaps worth noting that any non-trivial ActionScript code will use higher-order functions, because it's an ECMAScript dialect much like JS and, also much like JS, uses first-class functions for event listeners. PHP has also had a crude approximation of higher-order functions for a while, and it gained closures more recently. -DavidMcLean??]

The most natural event syntax looks something like this:

     event (attrib1=foo, attrib2=bar) {
       event_code_here();
     }

{Note: This is actually valid syntax in ScalaLanguage. The body is a curried block parameter. Suitable function definition assumed. }

That's really what the industry wants for "typical developers". The oddly nested HOF-leaking JavaScript syntax currently found is just a hiccup in history that will correct itself someday when historical patterns catch up.

The above style is optimized for the event writer, not the event engine implementer. You have it backward because your kind focuses too much on guts, gears, and bits instead of typical developer WetWare. -t

[Your complaints appear to be tied to language-specific syntax issues, rather than to the general question of whether higher-order functions work as a natural event-listener specification mechanism. The syntax you offer is entirely achievable with higher-order functions, just not in standard JavaScript. Examples below. -DavidMcLean?]

# RubyLanguage for example
event (attrib1: foo, attrib2: bar) {
event_code_here
}
# or, equivalently, if you like do/end style blocks:
event (attrib1: foo, attrib2: bar) do
event_code_here
end
# or in CoffeeScript with SyntacticallySignificantWhitespace:
event attrib1: foo, attrib2: bar, ->
event_code_here

I didn't dispute that they were achievable with HOF's; that wasn't the issue. There may be a lot of ways to implement event code. I don't really care about the implementation, though, whether it be HOF's or gerbils under the hood (as long as I don't have to clean up their droppings). And see the "syntax error" issue near the bottom of WhyWeHateRuby. (And I don't claim to hate Ruby, it's just a specific issue I'm referring to.) Further, a language that's too flexible in inventing syntax invites excess creativity of the kind described in GreatLispWar.

[Your fascination with gerbils is odd. What's up with that? As for there being lots of ways to implement event code, yes, there are. An optimal design for event-listener implementation will however avoid introducing more NonOrthogonalLanguageFeatures; if the language already has functions, then introducing a new non-function thing that represents a block of code (to be run when the event fires) is non-orthogonal, so an optimal design will use the language's existing functions as event code blocks. Closures aren't strictly necessary but are certainly very convenient and, in a language that already has LexicalScoping, not providing closure support violates the PrincipleOfLeastSurprise. If your hard-coded event syntax doesn't use regular functions as event-listener blocks, it's non-orthogonal; if it doesn't capture variables from the surrounding scope, it's inconvenient. If it does both of those things, you've effectively hard-coded the higher-order function design. -DavidMcLean?]

Full-out NonOrthogonalLanguageFeatures leads to something like Lisp, so we are right smack back to GreatLispWar: lather, wash, rinse, repeat. Ruby is just over-complicated Lisp with more syntax dingle-berries to play with.

[Which would be a problem if a GreatLispWar existed, huh? -DavidMcLean?]

[See, here's the thing. You say "The above style is optimized for the event writer, not the event engine implementer. You have it backward because your kind focuses too much on guts, gears, and bits instead of typical developer WetWare." But I am an event-writer, not an event-engine implementer, and I'd still choose higher-order functions for registering event listeners, with absolutely no hesitation. The orthogonality in features it affords, along with the consistency and flexibility of LexicalScoping closures, renders this method the cleanest and most capable way to handle such things.]

[I'll admit it's certainly true that writing complicated, nested asynchronous code using "raw" callbacks in ContinuationPassingStyle is a mess. However, with a hardcoded event syntax such as that you recommend, one is stuck with manually writing ContinuationPassingStyle code, because only your event construct is allowed to accept code blocks. If any function is allowed to accept blocks (i.e., higher-order functions are available), you can build higher abstractions over the basic event structure: I've already mentioned async.js ( http://github.com/caolan/async ), which abstracts out a wide range of common event patterns. You can also build higher abstraction by reifying the "result of an event" object as a promise, as is exemplified by the Q library ( http://documentup.com/kriskowal/q/ ) for JS. Going even further, you can represent events as streams and use FunctionalReactiveProgramming; for that, see bacon.js ( http://github.com/raimohanska/bacon.js ).]

[All of these abstractions make complex event-based programming clearer and simpler. None of these abstractions are possible if events are a hard-coded language construct. -DavidMcLean?]

Clearer and simpler to whom?

[It's to the developers writing event-based code, of course. -DavidMcLean?]

But you code funny.

[According to you. Note that I didn't invent async.js, Q's promises, or FunctionalReactiveProgramming, however, so I'm hardly alone in coding in a fashion you deem funny. -DavidMcLean?]

Time will tell if it's a fad or a trend. And I haven't seen a practical use for such for CBA's. Some claim it improves performance/machine-efficiency, but even if true, machine efficiency is often not the main driver of CBA's. PlugCompatibleInterchangeableEngineers trumps it. If somebody complains about performance, then extra tuning is done. If the DBA side is still slow after tuning, then the DBA is brought in to help the developer.

Whether you need async.js, Q's promises, FunctionalReactiveProgramming or not, there is a vast development world out there that uses them and finds value in them. That's why they exist.

Different niches may have different needs. That I never disputed.

[For the EventDrivenProgramming niche, the above abstractions are tremendously valuable in producing short, clear, expressive code. Outside of EventDrivenProgramming they're pretty much useless. You do recognise the value of EventDrivenProgramming, though, clearly, so the value of abstractions contributing to it should also be clear. -DavidMcLean?]

I invite you to illustrate "tremendously valuable" with realistic CBA scenarios. (You should have smelled that coming. Anti-kudos.)

[Alright. Give me a realistic custom business application based on EventDrivenProgramming to work with, and I'll see what I can do. -DavidMcLean?]

It's usually found in GUI's.

So all GUIs are realistic CBA scenarios??? You can do better than that, surely. You're the one always imploring us to come up with "realistic CBA scenarios". Now we're only asking you to describe one; you don't have to implement it.

Where did you get the "all" from?

Are you avoiding describing a realistic CBA scenario?

Are you avoiding answering the "all" question? I'll try to find a suitable scenario.

I was asking you, because you wrote "it's usually found in GUI's" -- which is an oddly evasive answer, given the context. You could simply have tried to find a suitable scenario in the first place.

Let's start with something simple: clicking on a button opens up windows to 3 search engines: Google, Bing, and Yahoo.

[Firstly, why? What business reason do we have for doing that? -DavidMcLean?]

[Secondly, it's too trivial. There's only one event involved in the system. FunctionalReactiveProgramming can simplify complex evented systems; there's nothing in this system to simplify. You can do it with just jQuery, like so:]

($ '#button').click -> ["http://google.com", "http://bing.com", "http://yahoo.com"].forEach window.open

[I suppose I could convert the events from the button to a stream, but it doesn't really get you any benefit in such a simple app:]

($ '#button').asEventStream('click')
  .onValue -> ["http://google.com", "http://bing.com", "http://yahoo.com"].forEach window.open

[It's just too trivial for higher abstractions to earn you anything. Not even a simple technique like adding functions can help you shorten that code (the first version, since obviously the FRP equivalent is a little longer when we're using basically no FRP features). -DavidMcLean?]

I welcome you to select a more fitting scenario for whatever it is you want to demonstrate.

[Sure. Since we're talking about search engines, I happen to have a search-engine-centric example of which I'm quite fond. Basically, it implements search-as-you-type using AJAX (think Google Instant). http://gigahard.mine.nu/~david/instant/docs/instant.html -DavidMcLean?]

You are focusing on the implemtation first. I tend to focus on the interface first, and then work backward. I'd start up by drafting an interface resembling:

  Name:  <input name="myTextBox"  simulsearcher="mysearch"/>
  <simulsearch name="mysearch" driver="foo" minchars=3
      refreshrate=2.5 maxdisplayrows=12 cussfilter="yes"/>
This is a very declarative interface in that I ask for what I want and don't have to worry about how it's implemented. Your approach is too low level, making one worry about function order, streams, etc. Now behind the scenes the implementation may use HOF's up the wazoo, but that shouldn't matter to the user of the service (app developer). Even if we are not or cannot use XML, Ideally one would only have to pass a list of attributes like the above and not have to worry about HOF's or call-backs or method chaining order, etc. The lower-level guts are showing. The interface should be entirely attribute-driven unless an imperative need is identified. And if one was identified, it could be defined like this:
  <simulsearch name="mysearch" onclick="myevent" etc="etc" />
  <event name="myevent" param1="blah" param2="blip">
<!-- event code and/or markup here -->
  </event>
I agree that implementation may be easier if we skip developer interface issues, but that doesn't necessarily mean we should skip interface issues.

[There is no such thing as a simulsearch tag. How is your design supposed to work? -DavidMcLean?]

     <!-- SAMPLE service-123 -->
     <input name="foo" ... >
     ...
     <service source="http://asdfasdfasd/blah.svc">
        <targetTags="foo,bar,naz"/>
        <eventsWatched="onClick,onChange,onTab"/>
        <etc>
     </service>

[As for imperative-versus-declarative, my code is almost entirely declarative. There are exactly two sections that are imperative: -DavidMcLean?]

results.onValue (res) -> ($ '#resultText').html res
ongoingSearch.onValue (show) ->
($ '.ajaxIndicator').toggle show
($ '#resultText').toggle !show

I don't find that very intuitive. Why does the service user have to concern themselves with toggle-ness?

[Because the way the service user chooses to represent the fact that there's an ongoing search depends on what interface the service user has designed. As the documentation noted, ongoingSearch just produces a true or false value; it's up to the developer to wire that Boolean value back into the view. It could easily indicate an ongoing search in some other way if desired, of course. -DavidMcLean?]

It should display a default spinner out of the box without intervention. One only has to adjust such IF the default is not sufficient. I'd probably define controlling such with something like the following Mark-2 example:

  <!-- Example: Mark-2 -->
  <service ...>  <!-- configure search-as-you-type service -->
     ...
     <eventHandler eventID="spinner-on" eventTarget="spon"/>
     <eventHandler eventID="spinner-off" eventTarget="spoff"/>
  </service>
  <event name="spon">
     <!-- markup and/or code to activate a spinner -->
  </event>
  <event name="spoff">
     <!-- markup and/or code to deactivate a spinner -->
  </event>

The event types (event ID's) are defined by the particular service.

Note that sometimes events may need to interact with the service, such as returning values:

  <event name="spoff">
     <!-- markup and/or code to deactivate a spinner -->
     <returnValue name="foo" value="bar">
  </event>

Services/addons would all follow the same style of interface for consistency. One then doesn't have to remember the proper method chaining order, whether to use HOF's, dollar signs, square brackets, or donkeys except for the relatively rare parts or times where one needs very custom adjustments. The interface would be nice and standardized as such:

(Names of tags are just preliminary suggestions only.)

[So am I right in thinking that the crux of your argument is that actually implementing stuff is always much too low-level? I provided an actual, executable implementation of a search-as-you-type feature; it's based on FunctionalReactiveProgramming streams and operates at a much higher abstraction level than an implementation based solely on callbacks would. I could've abstracted the stream-building into a function, like this http://gigahard.mine.nu/~david/instant/docs/refined_instant.html , as well, but doing so has minimal effect on the parts of the code relevant to my FRP-demonstrating example.]

[You seem to think we can prepackage all these things and therefore never concern ourselves with their implementation, but the fact remains that for search-as-you-type to work someone has to implement it. Arguing that tools for abstraction in implementation code aren't valuable because you don't think you should have to work with implementation code is absurd in the extreme; if we're going to imagine an environment where search-as-you-type is already implemented, why not imagine an environment where the rest of our application is already implemented and we don't need to program anything at all? If programming isn't needed, then obviously FunctionalReactiveProgramming isn't going to be of much use either; in reality, however, we do still need to write code to do stuff. -DavidMcLean?]

I made no assumptions about it already existing in the later samples. And I am not sure what the rest of your statements are about. Implementation and interface are generally two different concerns. True, one can dictate the other, but when designing the app programmer interface, we first focus making the best interface possible. The implementation in theory shouldn't dictate the app dev's interface, other than the features available. You seem to want to make the implementors' job easier at the expense of the app dev's, making their interface be a lower level or uglier. Perhaps there's a trade-off involved, but we should be aware of the trade-offs and understand their implications rather than tell the interface user to go to hell or the implementor to go to hell to make the other side simpler.

[App devs need to implement stuff too. Making an implementer's job easier with higher-level abstractions is of use to all programmers, because all programmers are implementers. In any case, the choice of higher-order abstractions involved in implementing a feature doesn't actually dictate the interface exposed for using that feature, so using a given abstraction is in no way a trade-off between making an implementer's job easier and making an app dev's job easier. Criticising my implementation of search-as-you-type as "too low-level" and presenting an interface to search-as-you-type as more intuitive is nonsense; if you don't have a more intuitive or higher-level implementation in mind, you've no grounds to criticise mine. Ideally, you should have a more intuitive, higher-level and also executable implementation. -DavidMcLean?]

What you presented is poorly packaged for an application-side developer because one has to know the "how" of it first. Developers don't have time to dwell on and twiddle around in your lovely underlying architecture: they want to get in, set the settings needed to get the job done, and get out quick. Maybe a big company with a large web audience for the search page may want to spend the resources to really dig in to fine-tune every bit and pixel, but that's the exception.

I'm not against being able to lift up the hood of the car and twiddle with the engine, but that shouldn't be the default or only interaction interface. Most just want to get in the passenger seat and drive the damned car using a the standard "dumbed down" dash board controls, not talk about turbo manifolds and multi-transduced fuel pumps. Back in my VB days I used to be able to download trial versions of most widgets from the component catalogs and a good many times get them up and running mostly by setting properties and filling in a couple of events. I didn't have to learn their internal architecture to make decent use of them. (Many were quite buggy, but that's mostly a different issue than developer interface design.) Maybe you haven't seen "interfaces done right" to know what "right" is. I don't know. In short, Make the common things easy and the rare things possible.

If you don't understand this, I cannot explain it in one or two paragraphs. You don't seem to understand humans and business. You are an alien life form and I don't know how to communicate with you without long and involved sessions. I'm at a loss for ways to explain it right now. I'll have to break from this and refresh my head.

[You don't need to understand anything about how the implementation works to use it. Note that the public interface, as described in http://gigahard.mine.nu/~david/instant/docs/refined_instant.html , exposes absolutely nothing about streams or AJAX or really anything to do with the internal implementation. A developer, hypothetically, could use it while having no understanding whatsoever of AJAX or event streams. The interface does require knowledge of jQuery, because that's the level at which I chose to abstract it: It takes jQuery objects as input and spits out events for wiring back into the DOM using jQuery again. I could've shifted the jQuery calls inside the function and taken more selector string arguments instead, but I preferred to abstract to the level I did, and I find it reasonable to assume a JavaScript dev has a basic understanding of jQuery.]

[While you don't need to understand how the implementation works to use it, you do have to understand how it works to appreciate that it's a more abstract, clearer, and higher-level implementation than one not using the high-level event abstraction of FRP is, however. Because that's the point of the example, I didn't concentrate on refining the public interface in the initial version of the code ( http://gigahard.mine.nu/~david/instant/docs/instant.html ), because the interface is in fact irrelevant to the purposes of the example; in fact, I didn't actually bother to expose an interface to the implementation at all. The "refined" version ( http://gigahard.mine.nu/~david/instant/docs/refined_instant.html ) does however have a proper public interface. If you must consider my implementation code solely in terms of its interface, despite that not being anywhere near the point of the example, I recommend you consider the aforementioned "refined" version mostly because it actually has an interface. -DavidMcLean?]

Well, I've seen what I consider well-packaged interfaces and poorly packaged interfaces over the years, and this falls into the second category in my opinion for reasons already given, such as exposing the spinner implementation when it doesn't have to be exposed (except as a last resort option). If you insist it's great, well, we'll just have to AgreeToDisagree and leave it at that.

As an exercise, give the feature list and ONLY the feature list to a skilled developer interface designer, and ask them to design an interface for it. They shouldn't have to see the implementation or the internal framework to do such. The internal framework dictating the interface is a yellow alert. It's fine if you have hooks for fancier stuff, but don't make that the default and try to control most features declaratively if possible.

[Again, the framework is not dictating the interface. I chose the interface exposed purely because that's the level of abstraction and flexibility I wanted it to offer. I could (quite easily) shift more jQuery-DOM-manipulation calls inside the function too, but I felt doing so would both be mixing unrelated concerns (event-stream transformation, DOM manipulation) and would reduce the flexibility afforded to a user, so I didn't.]

  Search: <input type="text" name="searchBox" onChange="SAYT"/>
  <div name="searchResults">...</div>
  ...
  <event name="SAYT" frequency=1.5 unit="seconds" startCount=2 stopCount=10 noDup="yes">
    ...
  </event>

[Now, please, could you consider the implementation a little? It is and always has been the whole purpose of the example. -DavidMcLean?]

Why? Because you don't want to be bothered to package it competitively?

[Because how it's packaged is irrelevant. The example is a demonstration of FunctionalReactiveProgramming in implementing complex evented systems. It is not a demonstration of my ability to design interfaces, nor should it be, because that would not further the point in any way. -DavidMcLean?]

I didn't ask for a demonstration of implementation techniques. This topic is about natural event usage.

[Quoth you, "I invite you to illustrate "tremendously valuable" with realistic CBA scenarios. (You should have smelled that coming. Anti-kudos.)" Because the implementation chosen does not dictate the interface exposed, it doesn't make sense to reject a demonstration of FRP's value in implementing a feature due to its interface (it's not hard to, say, use yet another different interface http://gigahard.mine.nu/~david/instant/docs/tops_brand_instant.html in any case). -DavidMcLean?]

You demonstrated an (allegedly) flexible implementation kit for SAYT, but ignored user-friendly (user is app dev). We appear to be right back to GreatLispWar were digestibility battles with flexibility.

[I showed a simple, clear, and absolutely (not allegedly) flexible way for an app dev to implement search-as-you-type. I then went on to show how you could wrap it up in a less-flexible interface (the latest one I posted) if you actually believe my initial versions aren't developer-friendly (I maintain that they are). In what ways does my most recent revision bother you? -DavidMcLean?]

I agree there may be a trade-off between "simple interface" and implementation flexibility. But I also pointed out that in theory one can still "get under the hood and monkey with the bolts" if the simple or default interface is not sufficient. Thus, it's a false dichotomy. (An exception may be if we want implementation swappability.)

[There is no trade-off between a simple interface and a flexible implementation, so you don't agree with me; as I've pointed out repeatedly, implementation does not in any way dictate interface. There is a trade-off between a simple interface and a flexible interface, however, hence why the "narrowed", simplified interface I offer in the most recent revision is also less flexible than previous revisions. It's no false dichotomy, because if you're going to make the interface more flexible it will by necessity be more complicated, whether or not the implementation has changed at all. You can certainly have an interface which allows you to ignore bits of it, deferring to defaults, or make use of the more complex options when required, though, of course. See http://gigahard.mine.nu/~david/instant/docs/blended_instant.html , but note, however, that that's hardly a simple interface: It's actually rather more complex than any of the previous iterations. Fortunately, however, that complexity arises from being liberal in what you accept, in that you're free to use callbacks, simple strings, or leaving it to use only defaults; in short, a more complex interface actually affords simplicity to a developer using it. -DavidMcLean?]


See also NaturalEventSyntaxDiscussion


AprilThirteen


EditText of this page (last edited October 15, 2014) or FindPage with title or text search