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.
- It's more accurate to say that it's what you want for "typical developers". You don't speak for the industry or "typical developers". You speak for yourself.
- Am I to assume that your characterization of what the industry or typical developer wants is more reliable? And you haven't proposed why an alternative would be better. If you set out to create the "best" event construct possible, focus just on events for now, how would you design it?
- I have not presented any characterisation of what the industry or typical developer wants. A convenient way to specify event handlers is to register a callback function. E.g., onLoad(loadFn) where loadFn is the function that will be executed when the Load event occurs. We presume, of course, that higher-order functions and closures are supported in order to simplify context references and concurrency.
- It's only an issue if it affects the behavior of an event block.
- [And it does, because alternatives to higher-order functions are incapable of providing closure support and hence restrict the use of local variables, such that switching to use such an alternative will break any event block that happens to reference local variables. -DavidMcLean?]
- Well, you've yet to provide a realistic scenario where it causes significant difficulties.
- [NodeJsAndHofDiscussion is packed with scenarios. (And argument.) In general, event blocks that can't access variables from their enclosing scope are needlessly and greatly restrictive; it becomes significantly more complex to perform otherwise simple event setup, registering multiple events in a loop (as the "Brady Bunch" example does) being a prime example. -DavidMcLean?]
- It's just a lab toy. Make it do something useful in CBA.
- [The Brady Bunch example is a real-world useful custom business application, Top. -DavidMcLean?]
- The specific client API favors HOF because it was built that way. A different client architecture could have favored other techniques. We've been over this already.
- [Yes, and we've also been over the part where we ask you to demonstrate that by building an implementation based on another technique instead, such as eval(), in order to show that there's no particular objective advantage to the higher-order function method over the eval() method; this included the part where we'd happily assume that setInterval()/setTimeout() take strings to eval() instead of functions to call, thereby addressing your API concerns. We however have not been over the part where you actually do that. -DavidMcLean?]
- I'm not quite sure what you mean. setInterval()/setTimeout() does not take such strings such that any setup would be hypothetical unless one rolled their own JS-able browser that did.
- [Yes. What we said was that you're allowed to imagine that they do---so yes, it'd be hypothetical. Since we'd really like your counter-implementation to be executable, as the existing Brady Bunch example is, you could write your own versions of setInterval() and setTimeout() that do take strings to eval() (and that then pass them to the built-in setInterval() and setTimeout() as functions), without too much difficulty. In fact, I've done that bit for you. Use these: http://gigahard.mine.nu/~dani/eval_timeout.js We're quite willing to ignore that the implementations of setSInterval()/setSTimeout() use higher-order functions, provided your eval()-based Brady Bunch example doesn't use any itself. -DavidMcLean?]
- Incidentally, NodeJsAndHofDiscussionTwo discusses a potential OOP design for such "timers".
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.]
- You are thinking in terms of re-use of concepts for other things or perhaps meta programming. I'm focusing on how to make events and only events as simple and strait-forward as possible for typical usage, not conceptual generic reuse. The market seems to prefer domain-specific or aspect-specific languages, as described in GreatLispWar. Can you admit at least there is a trade-off between optimizing syntax for specific patterns versus being generic?
- [Of course there's a trade-off, but unless you are actually writing a domain-specific language, you should always err towards the more generic patterns, because in a GeneralPurposeProgrammingLanguage the more generic patterns are invariably more valuable. -DavidMcLean?]
- The industry doesn't really want GPPL's, as described in GreatLispWar.
- [Even if we accept that a GreatLispWar actually exists, the fact remains that the industry absolutely wants general-purpose programming languages. Observe that the majority of custom business applications are written in general-purpose programming languages, specifically C#, Java, VisualBasic, JavaScript, and on occasion Python and Ruby. -DavidMcLean?]
- Didn't we debate this already somewhere? And it's not just about languages, its also about techniques.
- You've not presented any evidence that the industry as a whole rejects techniques such as HOFs.
- Similarly, you haven't presented any evidence that typical developers have any affinity for them. It's just anecdotes versus anecdotes. I'm pretty sure given a vote, most would take the above event syntax over the HOF version.
- [You mean the above syntax that, a few paragraphs later, I demonstrated was completely identical to the higher-order function version? That syntax? -DavidMcLean?]
- I'm skeptical it would give meaningful error messages in many situations, but that's something that would need more field testing and experiments.
- What is the real-world basis for your scepticism?
- I've seen such complaints from others when "roll your own DSL" syntax is used.
- What do higher-order functions have to do with "roll your own DSL syntax"? What do "roll your own DSL syntax" or higher-order functions have to do with "meaningful error messages"?
- Ruby-like DSL's are not as "tight" as dedicated DSL's. Errors and related messages allegedly sometimes expose the inner workings rather than give a cleaner message that "understands" or is closer to the native DSL constructs. Anyhow, let's put that claim aside for now and assume your Ruby-built event block and related errors are all just dandy and it works as expected. You've essentially created a DSL or at least a DS block construct. Congratulations!
- However, we could swap that implementation (HOF) for a different implementation, such as objects, and in theory the app writer wouldn't know the difference. Your HOF is now a low-level implementation detail that can be swapped out for another implementation. The app writer still doesn't have to delve into the world of HOF's.
- [There is no way to create custom block-accepting functions without them being higher-order functions, because block-accepting functions are a type of higher-order function. If we built up an event-specification DSL in Ruby based on the above syntax possibilities, it would by necessity be made of higher-order functions, and we couldn't swap them out for a different implementation because event specifications by necessity must be constructed from code blocks, and we cannot create functions, like "event" above, that accept code blocks yet are not higher-order functions. We certainly could replace the use of BlocksInRuby with FunctorObjects, but we can't do that without changing the syntax; and, again, a language with sufficiently sugared syntax for FunctorObjects is a language with higher-order functions. -DavidMcLean?]
- They could be eval'd strings ;-) In a "dedicated" language they'd be machine code pointers to a memory address, which I suppose is the low-level equiv of a HOF.
- [But, even assuming you don't need to change the syntax, switching to either of those will change the language's semantics, because eval'd strings and function pointers are incapable of closing over variables in their lexical environment; actual higher-order functions such as BlocksInRuby are also closures and hence have this feature. -DavidMcLean?]
- But do they need to?
- [To avoid violating the PrincipleOfLeastSurprise, yes, they do. That's how functions-defined-inside-other-functions work. It's not how eval'd strings or function pointers work, so changing from higher-order functions to one of those necessitates a change in semantics. -DavidMcLean?]
- Your argument seems to be "we should do it my way because we always did it my way before".
- [Please explain why it would be valuable for functions defined lexically inside other functions not to be able to reference variables from their enclosing scopes. -DavidMcLean?]
- {If I was executing ad-hoc code from an external source, I'd prefer that the eval not bind to anything in its environment. (Actually, I'd prefer to be able to say what it was and wasn't allowed to bind to.)}
- [Reasonable but non-sequitur. What environment an eval() expression is exposed to is completely orthogonal to the question of whether functions defined lexically inside other functions are permitted to reference and close over variables from enclosing scopes. -DavidMcLean?]
- {Sorry, I lost track of the context. But I would like to see a language that allowed me to specify what the function would bind to. C++ is the only one I know of that gives you anything beyond choosing either lexical or dynamic scoping, and even it only allows you to specify a subset of what's in lexical scope.}
- [True, arbitrary manipulation of the scope available to a given function would be a handy feature. I'd still want scopes to operate lexically by default though. -DavidMcLean?]
- {This isn't quite correct, closures might be higher-ordered functions, but they also might not be. The function you are passing the closure to (or returning the closure from) is the HigherOrderFunction. Function pointers are a hack to get around the lack of function values, and eval'd strings are just functions encoded as strings.}
- [Okay, yeah, I was oversimplifying a little. BlocksInRuby aren't actually higher-order functions: They're a syntax for passing an anonymous function (the block) into a higher-order function, and the anonymous function works as a closure. The fact remains that function pointers and eval'd strings, unlike anonymous functions (or named functions created inside the scope of another function; the anonymity isn't important to the LexicalScoping property), are incapable of capturing their lexical environment, however. -DavidMcLean?]
[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 ).]
- That assumes one wants to use ContinuationPassingStyle.
- [Actually, it doesn't. It only assumes that you want to make an asynchronous call based on the output of another. -DavidMcLean?]
- Can you demonstrate a practical use for such?.....or should I resist the temptation to ask this time?
- [Yes. See practically any code example on NodeJsAndHofDiscussion. -DavidMcLean?]
- SummaryOfHofExamples reruns?
- [Possibly. To be more concrete, making a request to a ControlTable and then using the instructions from that control table to make another database query would be a simple example of making one asynchronous call based on another's result. -DavidMcLean?]
[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?]
- It should be part of the standard GUI markup kit. If not, then perhaps add-ons should be permitted such that custom attributes can be added to predefined tags, both permanently and for a "session", depending and need/usage. If there is a potential conflict between vendors or kits, then a "driver=..." attribute perhaps can be available. An alternative is to define a standardized way to "register a service" on a given tag:
<!-- SAMPLE service-123 -->
<input name="foo" ... >
...
<service source="http://asdfasdfasd/blah.svc">
<targetTags="foo,bar,naz"/>
<eventsWatched="onClick,onChange,onTab"/>
<etc>
</service>
- [We do have a way to "register a service" on a given tag. It's called an event callback function. As for search-as-you-type being part of the standard GUI markup set, sure, you could do that. But can you really feasibly identify every single element of a graphical user interface that might ever be required by anyone ever? It's invariably better to provide generic, composable components from which features such as search-as-you-type, infinite scrolling, and so on may be constructed. -DavidMcLean?]
- The above addresses the case where it wouldn't be built into the standard kit. And clarity/intuitiveness versus "generic" gets right back to the heart of the GreatLispWar. I haven't looked lately, but there used to be an extensive catalog of plug-in Visual Basic widgets. A search-as-you-type widget could be a variation of a text box that you drag into place. You then fill in a various attributes ("properties") such as refresh rate, and select from a pre-set list of search source-types, such as database, CSV file, XML file, web service, etc. XML can generally give us the textual equivalent of such. If the attribute-driven approach wasn't sufficient, then the widgets also tended to include API's to customize the search more. It's kind of a hierarchy of high-level attribute-driven app developer interface with incrementally lower levels exposed/available if needed for more lower-level adjustments or customizations. You have the low-level interface/API up front that forces the app developer into the deeper guts too soon. HOF's in the lower-level are okay with me, but shouldn't be default app dev interface. Objects are plenty sufficient for the API and better understood by most app dev's. It may make the kit implementator's job a bit harder, but so be it.
- [Okay, so we hard-code as many patterns as we can manage into the standard set. Now what happens when we want to build something that's outside that standard set? This kit's apparently only providing a low-level API, and without tools for abstracting out that low level (FRP being one example) we'll end up with loads of AccidentalComplexity in implementing our custom widget. -DavidMcLean?]
- I did not suggest hard-wiring everything up front into the standard set. You misread something.
- [Misinterpreted, perhaps. By "hard-code as many patterns as we can manage", I was referring to your reference to the "extensive catalog of plug-in Visual Basic widgets"; we'd want the standard set of Web widgets to encompass as many GUI patterns as we can manage, no? -DavidMcLean?]
- Ideally we'd want our standard web GUI engine to either handle a wide set of typical GUI activities, or pre-define easy ways to plug in semi-common GUI needs. However, we also should consider and XML interface for hooking up unanticipated custom plug-in's/widgets (both lasting and temporal), and perhaps standards for getting into the deeper guts, maybe JS and DOM, for deeper control. The idea is to only go outside of XML for the portions that cannot be done in XML and its GUI widget and hookup conventions. None of the widgets in that VB addon catalog were anticipated up front, but VB's architecture defined a way to add such into VB and use VB's approach to specifying attributes and events/call-backs without having to deal with low-level code except for the parts or time where you need fine-tuned customization. You only peel to the core of the onion(s) only if you really need to. If designed right, only portions would typically need to farm out to mid or lower-level code, not the entire widget. The widget authors may define say 10 different events for dealing with search-as-you-type, and you'd only need to redefine/override those events that are at issue (perhaps with an ACTION or EVENT tag in our sample XML here) because the defaults and/or attributes make most of it automatic or have satisfactory defaults.
[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:
- [Out of the box? There's no box. I just coded that thing from scratch, and it does display a spinner without further intervention because I coded it to. Obviously, without any code to display a spinner, it's not going to display a spinner, so I can't just remove that code. I could perhaps have hidden away the spinner-displaying calls to some extent, since for some reason you don't like them, but I think keeping them clearly delineated and visible is preferable, since it's easier to find what you need to change if you do need it to work differently in some way. -DavidMcLean?]
- So you put all the guts on the outside in case one needs to change them? That's bad interface design in most cases. If you put some guts on the outside and some on the inside, what rule do you use to make that determination? It seems pretty clear to me that ideally there is a default spinner out of the box: the kit user shouldn't have to do anything or read through odd code to get the default spinner. However, if they want to control it themselves, then offer two events to define/override: turning it on and turning it back off, similar to the Mark-2 example. (Some middle-ground attributes could also optionally describe where the spinner goes and which image(s) to use for the spinner. These wouldn't require defining events, only attributes, and the attributes only relate to "what", not "how".)
- [Again, there is no box. There cannot be a default spinner out-of-the-box because there is no box; that's all the code involved in implementing the feature, so it must somewhere include the code to configure the spinner, and those calls are the code that does that. I could've exposed separate spinner-on and spinner-off events, like you suggest below, but no matter what events or interface I expose the fact remains that I will still need the code somewhere for the feature to work. -DavidMcLean?]
- There are or should be ways to hide that snippet of code if one wants to use the default spinner. Yes, the implementation of the spinner must exist somewhere, but shouldn't have to be in typical setup code used by the app developer. Put the guts on the inside unless there's no other choice or as optional plug-in spots.
- [Hide that snippet of code where? We're not using Visual Studio; we're using plain text. I can't just fold away bits of code that you don't want to see. In the original version of the code, the spinner is, like the entire rest of the code, wrapped up in the implementation, where the user has no need to be concerned with it. The same is true of the "tops_brand" version; the "blended" version has spinner details wrapped up inside but allows one to replace them if needed. -DavidMcLean?]
<!-- 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:
- Service <SERVICE...>
- Events (event ID's) <EVENT...>
- Event input attributes (if any) <EVENTATTRIB...>
- Event output attributes (if any) <RETURNVALUE...>
(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.
- [I would prefer not to allow people to drive the car from the passenger seat. -DavidMcLean?]
- Which fortunately is not what I suggested.
- [Yes it is. "Most just want to get in the passenger seat and drive the damned car". -DavidMcLean?]
- There are different levels of abstraction. 1) There's the passenger, 2) the vehicle driver, 3) the basic mechanic who opens up the hood/vehicle to replace whole parts, and 4) the parts builder(s). If you are "nice" to your customers/users, you make it so they can stay at the highest level possible to get whatever service they need. You want to reduce the probability that they will have to go to the next deeper level to get satisfactory service.
- {Where does the driver's seat fit into this? That's the seat with the steering wheel and the pedals, in case you're forgetting. Or are you suggesting they just tell the car where they want to be, and it drive itself?}
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.]
- A "service" has to provide a service, and such may be required to be involved in many different "concerns" to carry it out. Your "unrelated concerns" viewpoint seems to be overdone. It's almost like saying to a home owner who wants a brick wall put up: "I'm sending you a different quote and bill for cement and for bricks because they are different concerns". Optional hooks to control each aspect, fine; just don't make it the default.
- [Okay, but I didn't implement a service. I implemented a function, simulsearch, which transforms search queries into search results. It quite deliberately only operates on a "behind-the-scenes" level to allow it to be dropped into pages with very different layouts. -DavidMcLean?]
- Why not? If we build our GUI standard around services, then we are less dependent on specific client languages such as JS or jQuery and perhaps hook them up to server-based services with only a change in the driver name/ID and/or service reference URL. If we treat everything as a web service, the web service could also optionally be running on the client, I would note.
- [Given our current design for the HtmlStack as a whole, GUI-type services necessarily are wired into the page using JavaScript. You seem to be assuming that your GUI-language markup proposal already exists and that you can therefore judge my search-as-you-type function by the standards of said markup proposal; right now, services operating on client-side, like a search-as-you-type utility, must run as JavaScript. -DavidMcLean?]
- Yes, but we are talking about how to do it right, not how to figuratively live with QWERTY because history screwed us over.
- [If you aren't planning to implement your doing-it-right, it seems far more valuable to focus on ways to make actually-executable methods clearer, simpler, and more capable. Non-existent facilities are intrinsically not as useful as existing ones. -DavidMcLean?]
- Sure, but we can explore both idealistic scenarios and here-and-now scenarios. The idealistic scenarios may inspire the next generation.
- [Even assuming your idealistic situation, how would we feasibly implement a search-as-you-type feature without doing so on the client-side? It's not something we can do purely in server-side code, no matter how fancy our markup gets. -DavidMcLean?]
- This topic is not really about server-versus-client side except that ideally we want to make such be swappable when needed by programming to an interface instead of an implementation. Our markup kit should make such easier in part by providing a service standard.
- SAYT can be implemented server-side by sampling the "search box" every N seconds and displaying the current best matches below it, or a "target results panel". Nothing about that is really outside of my markup plan. We've already gone over tags like <event ... repeat="3.2" unit="seconds"> in the "Brady Bunch" examples. The event grabs the current value of the search box, queries the server, and the populates the target search results panel. That's all common gui behavior. The only thing we haven't covered is how to deactivate the service or querying when we are done, but the approach depends on the interface design. I can think of multiple ways.
- [Polling the server with the current query every N seconds doesn't even slightly provide search-as-you-type functionality, because it has two glaring flaws. The first is that it must by necessity send and receive network data every N seconds, whether the box's content has changed or not; this means that it'll have far more transmission overhead than a real search-as-you-type feature that sends data exactly when the user types and at no other times. The other problem is that it must send and receive data only every N seconds: Unless N is really really small (say, 0.5 seconds), it won't be search-as-you-type, because it'll instead be search-after-you-type. If it is a really really small N, that amplifies the other problem, that of large amounts of strictly unnecessary data being transferred: Since it's sending to the server that much more often, there's that much more data transferred. A true search-as-you-type feature cannot work via this sort of polling; it will only work if a "user has typed" event is employed at the base level rather than a "time has passed" event, and when you have such an event it's trivial to transform it into proper search-as-you-type, as I've shown, so there's little point putting effort into server-siding it. -DavidMcLean?]
- The server could help by not sending a results refresh if the text is the same. An event could also have a "stopCount" and "noDup" attribute. The stopCount would limit the number of times the event would fire. The "noDup" attribute means that same event cannot be relaunched if its already active. Thus, the search box's "onChange" event launches the refresh event "loop". Subsequent onChange events don't launch any new search events for the given time because of the "noDup" attribute. (The "startCount" is used to delay a bit before first search to give one time to type.) These are reasonable features for a repeating event mechanism to have.
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>
- Another approach is to have the server shut off the event using a simple delta markup update after a series of sameness, but this requires a reliable server connection.
- [But these additions both don't solve either of the problems and are needlessly complex. Neither problem is solved because fundamentally you're still polling every 1.5 seconds, which means you're still sending needless requests; granted, you're sending fewer needless requests now because you stop the "loop" after ten, but the fact remains that they're still needless. Because you're still only polling every 1.5 seconds, it's also still not search-as-you-type, since it'll often only search after you type. I say it's "needlessly complex" because you triggered your polling event using an onChange event, which means you have an onChange event, which means you do not need to poll. If you have an onChange primitive event, transforming it into a polling timer event is useless, at least for this particular problem; in fact, a polling timer is only ever useful when you need to check a property for which there is no primitive event, since if you have an event for it it'll invariably be simpler and more efficient to use it. In this case, why transform the onChange event to a timer event, when it's infinitely more valuable to transform the onChange event into a "search results received" event (see my implementation for an example of that)? -DavidMcLean?]
- How are you measuring "complexity"? I just have to change a few attributes. Your thing is a screwy mess. You have too many poorly-described claims jammed into one paragraph there. I agree that if we want a well-tuned optimized gizmo, we'll probably have to do or get some client-side programming. But for a quick-and-dirty low-usage situation, such may be good enough if SAYT is not built into the interface/GUI-tool, which it can be. But this digresses from the idea of having a standard way to hook in components without having to use or write app-language-specific code, or at least as little as possible. What of SAYT can be defined via attributes, and what parts could be optionally given events to override or customize behavior when desired?
- [Complexity is measured pretty much in terms of parts involved. My design has the following: a stream of search queries, a stream of search results (a fairly straightforward transformation of the query stream), and a Boolean flag indicating ongoing search (a trivial combination of the first two). Yours has the onChange event, which is analogous to my query stream; for some reason, you then trigger a polling timer in response to that event source, which polls the search field at regular intervals, sends the info to the server, and presumably listens for and handles responses. Effectively, you have an extra event source (the timer) for no particular reason, which is more complex than having a single event source. -DavidMcLean?]
- In this case I need something like 15 lines of code and you needed a gizmo that has something like 5,000.
- [… huh? My initial implementation is thirteen lines. The later ones are longer, because you wanted a more flexible interface; the longest and most complex, blended_instant.coffee, is a little heavier at forty SLOC. Where'd you get 5000? -DavidMcLean?]
- You are relying on jQuery.
- There's probably over 750,000 lines of code you're using in a Web browser, just to edit this page. It'll take far fewer lines of code if you simply telnet to port 80 on Ward's server. :-| Anyway, how many lines of code do you think it will take to implement your <event name="SAYT" frequency=1.5 unit="seconds" startCount=2 stopCount=10 noDup="yes"> ... </event> syntax? I bet it's more than 15.
- That's one of the big problems with code counts: what in the "libraries" counts and what do we define as a "library". Anyhow, this sub-topic is kind of a side issue anyhow. Implementing SAYT is secondary to "packaging" issues. My concern is hooking up "add-ons" here, not how to implement SAYT, and exposing events in an orderly and consistent way is one approach.
- [Library code doesn't count, because you don't have to maintain it as part of maintaining your application; you only need to reference it in a list of dependencies somewhere. jQuery is a complex and indeed very long library, but in using it for this app and others I have never actually seen its source code, nor will I need to. If you're going to count its lines as part of my app, you really should be counting the lines of code used to implement your GUI markup language as part of yours. As for hooking up "add-ons", I've been pointing out that your method for doing that won't actually work for search-as-you-type. Shouldn't that be considered and addressed? -DavidMcLean?]
- You didn't prove it wouldn't work, but that's a side issue anyhow and probably should be split off to another topic or something. Even if it didn't, it's a distraction from the main point because I didn't forbid add-ons. I'm proposing a standard interface/conventions for add-ons based on lessons learned from good and/or easy-to-digest interfaces: paint-by-numbers for in-hurry or not-to-bright developers. Essentially it's a hierarchy of:
- 1. Most usage controlled by setting attributes (and only attributes if possible).
- 2. Override-able events for finer control
- 3. Using a direct API and/or app language for finer control if above doesn't cut it for our needs
- [But your standard interface for add-ons isn't able to provide support for search-as-you-type. Isn't that a sign that you should modify it? -DavidMcLean?]
- Can you describe a flaw-exposing scenario? I need more specifics.
- [I did, above. We want search-as-you-type. If we implement it using polling every few seconds, then it's not search-as-you-type: It's search-after-you-type. If we shrink down the polling time to a more reasonable range for giving the appearance of search-as-you-type (0.5 seconds is good), then we end up making way more requests to the server than we should really need to. You can impose event "loops", limits, and triggers upon the process, but they don't really help; what does help is basing the search-as-you-type functionality directly on the text-box's onChange event rather than on a polling timer. -DavidMcLean?]
- You seem to be confusing the "sample implementation" via draft GUI markup and the "interface" issue. The interface issue does not define an implementation and thus cannot have an implementation flaw by definition. On my sample implementation, I put an initially delay because we don't want it searching when only one character is entered (unless paused on for 1.5 seconds in the sample). That's usually useless. (The first char would trigger the onChange event). Note that JS sometimes acts "jittery" for refreshes under a second on some computers. I don't know why, I only know that JS-GUI's are unpredictable and suck in browsers.
- [But we do want it searching when only one char is entered. We want it to search as we type, and we can do that using the onChange event. Why are you championing a timer-based event that polls every 1.5 seconds, instead of simply using the onChange event directly? What benefit does that afford you? -DavidMcLean?]
- I don't believe JS in the HtmlStack is powerful enough to do it right. Even google's SAYT can be jittery and goofy, even on new hardware. I've learned to live with the quirks over time, but when billion dollar companies with excess cash screw up JS GUI's, it doesn't contribute much to the hope for mom and pop or department-level programmers. Anyhow, I choose to give JS plenty of "time" if the implementation decision is mine. You can push it to the limit if you want, but YOU answer the fricken help desk. (Google has improved it over time.)
- [Uh… seriously? You seriously believe JavaScript isn't powerful enough for search-as-you-type? Even though Google have done it? Heck, even though I've done it? -DavidMcLean?]
- Please re-read. I doubt you've tested it on sufficient platforms and hardware.
- [I don't need to; I've used solely platform-independent libraries and code. The same is true of Google's implementation. -DavidMcLean?]
- It's not 100% platform-independent. It gets more so over time as dependencies are fixed and work-around to bugs added, but never 100%. The only way to be sure it works on every browser vendor and version is to actually test all features on every vendor and version. A given version may have a bug, such as a memory leak. Yes, it's a PITA, but that's the way the HtmlStack is.
- I remember when testing on every target platform was a standard requirement for C and C++ programs. Oh wait -- it still is. We probably come closest to true cross-platform capability with Java and Python, but cross-platform testing is still necessary.
- Big Internet-dependent companies often have a testing lab with a dozen or so computers all with different OS versions and browser versions.
- [Absolutely true, but how's that relevant? Whether a given app works on a given OS or browser is wholly a client/platform-specific issue, and you've made quite clear that you aren't interested in such issues. -DavidMcLean?]
- You are misconstruing me. I said they usually make poor examples, not that cross-platform isn't important. Jquery is only half-ass cross-platform. Then again, everything probably is to some degree.
- [In the sense that it only works in Web browsers, I suppose jQuery is indeed "half-ass" cross-platform. What else would it need to work in, though? -DavidMcLean?]
- Just like your memory-leaking Brady Bunch demo?
- [I didn't have a memory-leaking Brady Bunch demo. You've confused me with someone else. In any case, you've avoided my question: Why would we want a polling timer event when we already have an onChange event? What benefit does the timer afford us? -DavidMcLean?]
- I have a "Brady Bunch" demo, but since only Firefox leaks memory it's not accurate to call it a "memory-leaking Brady Bunch demo", it's a "Brady Bunch demo that also runs on that memory-leaking Firefox." Anyway, yes Top, what does the timer afford us? How is it preferable to an onChange event?
- See above. As far as using timers versus onChange, that's a design decision of the designer. Actually, perhaps ideally the choice should be a config switch as each approach may work better for different query sources and situations.
- So you're retracting the polling-based approach you advocated above?
- No. Allowing both approaches is not "retracting".
- But using polling for search-as-you-type is entirely unreasonable. There are places where timers and polling are appropriate, but search-as-you-type is not one of them.
- Using only onChange can create problems also. I know a few search servers that would probably choke if a few people queried something long at the same time such as "Mississippi legislation investigation". A way set a minimum threshold frequency for server requests would probably be warranted. The result would probably resemble a hybrid of polling and onChange. I'm getting bored with SAYT implementation discussion. It's not related to my purpose to be in this topic. By the way, JS seems happier on the client-side also if not overloaded. Addons such as spell-checker tend to work better if there is throttling of JS/DOM event frequencies. (Spell-checking is a form of SAYT, I would note.)
- [There is throttling in my implementation of search-as-you-type, limiting requests to 500 ms or 0.5 seconds. There's absolutely no polling, however; polling remains useless for search-as-you-type purposes. As for this being unrelated to your "purpose to be in this topic", you did ask for a demonstration of FunctionalReactiveProgramming. If you in fact have no interest in seeing a demonstration of FRP, go ahead and shift the topic back to whatever your original purpose was. -DavidMcLean?]
- If stopCount is set low, it's almost the same behavior. I asked about FRP in terms of making a convenient plug-in interface, but your idea of "convenient" isn't.
- [No, you asked about FRP in terms of it being, as I claimed, "tremendously valuable" as an abstraction; there was at the time no discussion of plug-in interfaces. If stopCount is set to exactly one, it's the same behaviour, but if we do that why even have stopCount, frequency, and friends? Your interface is inconvenient in that it assumes polling will be required, when for many evented scenarios---in fact, I might even say most evented scenarios---polling is completely superfluous. There's also the problem that you've not shown us what goes inside the <event> tag yet: All you have so far is your markup equivalent of "onChange, do …". What goes in the …? I've shown how one can transform an onChange event to searching-as-we-type using FRP. How do we get from an onChange event to searching-as-we-type under your system? -DavidMcLean?]
- There's a difference between implementing it with the existing hypothetical GUI markup features and using a standardized interface of a GUI markup to "call" outside services and/or add-ons. My later examples were only to show it's possible to use existing features, but that may not be the most optimized (assuming SAYT is not built in). The standardized add-on interface approach is illustrated around sample "service-123". How your claims related to such is what I was curious about, whether I made that clear or not. If not, I apologize. Remember, I was motivated by the add-on catalogs of VB, Delphi, etc. in terms of how they packaged/standardized the interfaces to avoid having to "expose the guts" of their add-ons as a first resort.
- [But such add-on interface approaches didn't exist on this page at the time my claims were made? As for calling outside services, that does not work for search-as-you-type, which must be implemented on the client-side; the same is true of many evented systems and user interfaces. There's already a standardised interface for communicating with servers (HTTP through AJAX), so what does this interface enable that regular HTTP does not, considering that it can't reasonably provide server-sided versions of features like search-as-you-type? -DavidMcLean?]
- Only parts of it have to be implemented on the client-side (if we want it be reasonably efficient). And we should also allow for the possibility of server-side-only SAYT even if it's kind of slow. The ideal I am looking for is to hook up to services/add-ons using ONLY the markup interfaces. Behind the scenes it may be indeed load/run AJAX or what-not, but the app builder shouldn't have to touch JS or AJAX code, staying only with the GUI markup. Occasionally one may have to twiddle with such if the standard interface is not sufficient, but we want to target roughly 80% or higher of actual uses being only through markup.
- [Well, yes, it's strictly true that only parts of a search-as-you-type system need to be on the client-side. However, those are already all the parts that are in my implementation: send queries from the search box to the server, receive results, show them. You can't have server-side-only search-as-you-type because you'll at least need something on the client-side sending the state of the text box to the server and handling the server's responses. As for your ideal, why? What's so great about markup that we'd want to give it the ability to do what JavaScript does already? -DavidMcLean?]
- The existing kit already supports onClick events and polling, similar to the one you balked about. One could use those to build server-centric SAYT without downloading JS; it's just not very optimized. But for low-usage it may be good enough. (The standard service interface would allow optional usage of existing tags for parts or all of the implementation, per service builder/definer's descretion.)
- [No, you cannot build server-centric search-as-you-type. No one can build a server-centric search-as-you-type facility. It will not be search-as-you-type if you do. You're solving a different problem. -DavidMcLean?]
- I don't want to get bogged down in definition issues here. In my experience most "search servers" could not properly handle the load or turn-around time of keyboard-speed SAYT and must be throttled to some extent. I'm thinking in terms of what would work reasonably well using typical servers as found in the wild. Maybe you are assuming client-side or client-loaded lists?
- This isn't a definition issue. You can't build a server-centric search-as-you-type, because you have to have code execute when each character is typed. Otherwise, it's not search-as-you-type.
- Show me the official definition where it must be for each and every key-stroke. A throttling mechanism would be very useful regardless of what we call it. I'm not going to make jittery junk just to fit an arbitrary definition. It often doesn't make sense to load the entire search space onto a client, meaning in practice we'll be dealing heavily with servers.
- [You have to run code as each character is typed because it's search-as-you-type. The only way to implement without running some code when each character is typed is by polling manually, and as discussed above you can't approximate search-as-you-type with polling without greatly increased network overhead; in addition, and also as mentioned above, there's no point in polling for changes to the text field because there's already a 'keyup' event for it, and polling is only necessary when you don't have an event representing a given change. It obviously doesn't make sense to load the entire search space onto a client, especially when it's significantly huger than the size of the search results, sure. However, there aren't any search-as-you-type systems under discussion here that do load the entire search space onto the client: My implementation and Google's both leave the search source on the server and merely query it through AJAX. -DavidMcLean?]
- "As" doesn't necessarily mean "always". Polling is an implementation issue/decision anyhow, not a definitional one. Again, a tool "similar to" what you are calling SAYT would best be practically served by having throttling options. If adding throttling options changes the damned name of it, so be it. Call it a Biffledorf if need be, I don't care. I want well-functioning stuff over stuff that fits definitions if these two goals conflict. If the definition police complain, kick them in the nuts because I'm going to use the Biffledorf. -t
- [Polling doesn't help you implement a Biffledorf either. Again, you have a primitive keyup event. The HtmlStack provides one. In an evented scenario, polling is only ever useful when you don't have a primitive event for a specific property, because the whole point of polling for changes is to approximate an "on change" event. Because you do have a primitive event here, there is absolutely no value in polling the field. Throttling is a different thing, however, and it's something that doesn't impact the search-as-you-type-iness of your Biffledorf: My implementation has mild throttling, yet is still a search-as-you-type implementation. As noted, though, a search-as-you-type implementation still requires that you run some code when each key is pressed, which my implementation indeed does (it changes the event stream to one of search queries and then throttles it, specifically). -DavidMcLean?]
- I was addressing definition issues there. Polling can be used implement a fairly crude version of Biffledorf, but I'm not claiming it's the optimum. I think we are intermixing too many issues here: definition, crude techniques without using JS (if simple action lists supported), and more "tuned" approaches that would require a library/widget.
- 1 -- By definition, search-as-you-type that isn't search-as-you-type, isn't search-as-you-type. It might be content-search-that-triggers-every-so-often-whilst-you-type, but that isn't search-as-you-type. 2 -- Stop quibbling.
- Meeee stop quibbling? Projectamundo. And again, there is no official definition, at least not that you cited. I'm building a Biffledorf. If you don't like my Biffledorf, tough cow snot; go ahead and flood out the servers with your keyboard iMachine gun.
- There is no reason for search-as-you-type to flood servers. As discussed above, it is (a) trivial to control the keystroke stream rate on SAYT (simply queue it, and the server empties the queue at its leisure), and (b) your polling-based strategy is more bandwidth intensive than keystroke-triggered SAYT.
- But then you are shoving queues to the server to process when it may not be ready. Polling would be only a mild increase because queries identical to prior are ignored. The volume of return results ("hits") will be roughly 50 times that of a rejected query, so we are talking only a few percent of bandwidth difference.
- Keystroke-triggered SAYT only sends keystrokes when the data has changed, which is exactly what you want to trigger a search. Polling-triggered "SAYT" sends the search string every time the timer is triggered.
- A good many people can type about 10 characters per second. That's too many queries for typical search servers.
- Having implemented keystroke-triggered SAYT in various systems, that has never been a problem even on low-end heavily-accessed servers. See above re "no reason for search-as-you-type to flood servers". A continuous stream of search messages, every 250ms (or whatever the polling rate is), is far worse than occasional bursts of new content.
- I don't trust you or your anecdotes. And why would the last sentence be the case? It's not more.
- Whether you believe me or not is irrelevant. Implement SAYT yourself using both polling and keystroke-triggers. Put both in production. See what happens.
- And vise verse with regard to polling.
- I've tried it. I know the answer.
- Apparently not enough to describe the reason and scenario of typical failure scenarios in useable detail. "Trust me, it sucks" is not very useful.
- Polling-based "SAYT" creates a continuous barrage of messages at the polling rate as long as the page is active, unless you only send to the server when the search string has changed. In that case, if the poll rate is too slow it annoys the user by appearing to "jump to life" at arbitrary intervals unrelated to keystrokes. If the polling rate is fast enough to overcome that -- and you're only sending content if the search string has changed -- then you're essentially rolling your own keystroke trigger but with a power-consuming continuous polling loop which is bad for battery life on mobile devices. So, if it's available, you might as well have used an onKeyUp (or whatever) event. In short, to avoid swamping the server with continuous polling messages, you either wind up with something that's annoying to the user or you create something equivalent to keystroke-triggers but with more code and bad battery life. Either way, polling loses. It's only an option if no more specific event is available.
- "as long as the page is active" is false. I put a "loop limit" on the poller (stopCount). (It's re-triggered by a key-stroke in search the box.) As far as the ideal poll rate, that's a tricky issue either way, and there are different approaches to managing that dynamically, both server-centric and client-centric. And if it's an intranet application intended for desktop users, then phone battery life is a very miniscule concern. Again, a more optimized one would indeed more careful programming. I was only illustrating a "good enough" solution without having to write JS.
- So you use a keystroke trigger to activate the poller, but don't use the keystroke trigger to drive the search? Why? That seems to be needless complexity, and how do you know when to turn off the poller? Why not simply use the keystroke trigger to drive the search, and let the server queue the requests and throttle them as needed? That's simpler, more responsive, and more efficient than using any client-side polling.
- Are you suggesting sending an HTTP GET for every keystroke at keystroke speed? If the query takes 5 seconds to process a typical query, then such may flood the processing queue unless special programming is done. As far as "needless complexity", it uses existing features of the GUI markup without JS. It knows to turn off the poller because the number of cycles is pre-limited, as already described. Yes, with enough programming on either side we can optimize the network traffic, but sometimes one is asked to get something up quickly and/or has a full in-box at the time. Tuning can come later. My approach wouldn't require any JS and a basic GET query with only the ability to ignore N cycles of the same query string from the same IP/session if the SAYT hidden value flag is set.
- [The poller is the needless complexity. Polling a value, as we've said innumerable times now, is completely worthless if you have an "on change" event for that same value; in general, the only purpose of polling a property in an evented environment is to emulate a native event for that property. In this case, we already have a native keystroke event, so there's no need to emulate one with polling. Triggering a polling loop every time the keystroke event fires, which then polls for query text and sends it, is needlessly complex because we can simply use the keystroke event directly instead. As for "sending a GET for every keystroke"… take a look at my implementation. Look properly, so that you notice that it has throttling support; as I've already said, polling is worthless but throttling is not. -DavidMcLean?]
- Needless complexity relative to what? Yes, but then you have to build/install a throttler on the server side.
- [Relative to a solution that doesn't have an extra polling loop for no particular reason? Mine, for example. As for installing a throttler on the server-side, nope, I don't have to. My implementation doesn't care what's on the server-side. -DavidMcLean?]
- [As for not requiring any JavaScript, sure, your approach doesn't require any JavaScript. It just requires that browsers implement an imperative language that uses XML syntax. There's no advantage to this over JavaScript; anyone disabling scripting in their browsers will expect that to include disabling of MarkupScript?, after all. -DavidMcLean?]
- HTML has imperative parts also. Imperativeness is on a continuous scale. JS would require downloading non-trivial libraries. Weren't you fussing about phone batteries recently?
- [The imperative parts of HTML are JavaScript. Users can disable scripting in their Web browsers, which currently disables JavaScript. If you add imperative markup constructs to HTML like <event> tags, that's effectively a MarkupScript? which should also be disable-able by disabling scripting. It doesn't buy you any advantage. -DavidMcLean?]
- HTML already has imperative actions.
- [The only imperative actions HTML has are in the form of JavaScript. -DavidMcLean?]
- I disagree and we've been over this already. We are LaynesLaw-ing over "imperative". Anyhow, I don't have to download libraries. That is at least one advantage.
- [Why do you think it advantageous to claim that a solution has "no JavaScript" when that is only possible as a result of adding JavaScript's features (and libraries) to HTML instead? -DavidMcLean?]
- [Also, what is your solution? All you've actually presented is the outside of an <event> tag. More than that is required to achieve search-as-you-type. At the least, what goes inside that tag? -DavidMcLean?]
- And why try to limit it to just markup and standard markup interfaces? Because first, most app builders don't want to have to learn screwy JS API's. Attribute/event-centric interfaces are quicker to learn and set up. I'd estimate it averages 1/3 the setup time for the catalogs I talked about when comparing the attribute/event interface versus direct API's. Maybe you have FastEyes or can absorb new API documentation quickly. I've met some people like that. If you are one of those, good for you, you get a gold star on your forehead. However, most developers want something easier to digest to save time. Further, in many cases developers just want to try out different widgets to see which works best or to generally explore (trial versions). Standardizing the interface makes them easy-come-easy-go'.
- [I would prefer not to learn any screwy APIs, whether they're in JavaScript or markup. I see no reason why markup would provide any improvement over JavaScript in designing APIs; both are pretty much equally expressive. If you're arguing not for markup specifically but merely for attribute/event-centric interfaces… well, the vast majority of interfaces exposed by JavaScript libraries are event-centric already, since that's an optimal way to express GUI code, and there's no reason JS couldn't be attribute-driven (a lot of it is!). -DavidMcLean?]
- I'm putting standardization over expressiveness. Yes, JS API's can be attribute and event based, but they have many different ways to specify such. Merely having that capability does not by itself standardize the way in which it's expressed. For example, one can use parameters, arrays, and/or object properties to specify attributes in JS. And you may use "regular" JS or jQuery style. And the order that one sets up events and attributes can matter and be many different ways in JS. In the catalog style, order usually didn't matter (because they are referenced by name). The catalog way allows one to specify them in a "linear" table/chart style. I don't have to learn where or how to specify them differently for each different service/addon. The chart alone is usually sufficient. It's paint-by-numbers. Yes, one could standardize JS calls in such a way, but if they were properly standardized, then one could put an XML wrapper around it anyhow.
- [In presenting a dichotomy between standardisation and expressiveness, you presume that XML is less expressive than JavaScript. It's not. XML encodes arbitrary trees; it's possible to express every single JavaScript style you mention, and with many XML designs order is still meaningful. In addition, you can't ensure that the interface to any given service is standardised simply because it's expressed as XML: As noted below, there's not actually anything in XML that can enforce that different implementations, even of the same service, expose the same interface. -DavidMcLean?]
- The key is standardization of interface, not so much that it's XML. Making sure the interface is XML-able does help weed out imperative-centric standard candidates, though. It's somewhat like the movie axiom, "If it plays in Peoria, then it will play anywhere."
- [Well, XML itself doesn't preclude imperative semantics. If that's what you're after, push for a Prolog or Haskell-based standard interface. As for the interface itself, it seems reasonable, but only if your interface design is good enough to encompass a wide majority of possible services. I've already observed that it doesn't seem adequate for search-as-you-type; have you since revised it? Complete examples of usage or a proper specification of what it involves would be welcome. -DavidMcLean?]
- The design is generally based on my experience with the widget catalogs I talked about. It was a pretty good balance between uniformity and flexibility and I've yet to see a better suggestion, at least for GUI widgets.
- And you haven't described why it's not adequate for SAYT? Your complaints were so far about a suggested implementation, NOT the add-on/service interface. It could be implemented using YOUR own gizmo.
- [I was under the impression that your <event frequency= stopCount= noDup=> tag was, if not the whole interface, at least a significant part of it. Such a tag definitely is not helpful for search-as-you-type purposes, or in fact for practically any evented purposes, because as noted polling is unhelpful and is superfluous in any case where you have an actual change event for a property. If that tag and your attempt to use it for search-as-you-type is actually completely unrelated to the interface topic, please actually show us your proposed interface, preferably with usage examples or a specification as requested. If indeed it'll work with my implementation, great! Show us how. -DavidMcLean?]
- I guess I wasn't clear; too many sub-topics on the stove at the same time it seems. I'll make a new topic to go through step by step, perhaps ServiceInterfaceDiscussion?. Keep in mind that the "events" defined for a widget would generally be called by the widget. For example, a good SAYT widget probably should have a default results panel such that the app writer doesn't have to define and draw their own (other than maybe supplying a target DIV). However, if the option for the app author to create the panel themselves was available, then the SAYT widget would call a specified event when it's time to redraw the results panel, along with a reference to a structure (such as an XML list) of the unformatted search results. The app developer can then use that info to redraw the results panel themselves. They don't have to use timers. -t
- [That seems pretty reasonable, except insofar as it sounds exactly like plenty of already-existing JavaScript interfaces and in fact seems almost identical to one of the interfaces exposed by my search-as-you-type function. What's actually different about your service interface? I'd still appreciate a spec or some examples. -DavidMcLean?]
- Second is swappability. Another vendor could provide a better cheaper and/or faster service/component with a same or similar interface so that we can swap it in when it comes. (Vendors tend to copy or follow similar conventions to steal customers.)
- [Why would using markup give you any such swappability benefit, if JavaScript doesn't? There's nothing in JavaScript that requires different implementations of the same service to expose different interfaces, and there's not really anything in your markup spec that actually enforces that different implementations of the same service expose the same interface (nor can there be, in general). -DavidMcLean?]
- See above.
[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