Path: csiph.com!newsfeed.hal-mli.net!feeder3.hal-mli.net!newsfeed.hal-mli.net!feeder1.hal-mli.net!weretis.net!feeder4.news.weretis.net!npeer.de.kpn-eurorings.net!npeer-ng0.de.kpn-eurorings.net!newsfeed.arcor.de!newsspool4.arcor-online.net!news.arcor.de.POSTED!not-for-mail Content-Type: text/plain; charset="UTF-8" Message-ID: <8935431.65aROmshh0@PointedEars.de> From: Thomas 'PointedEars' Lahn Reply-To: Thomas 'PointedEars' Lahn Organization: PointedEars Software (PES) Date: Mon, 07 Jan 2013 03:58:39 +0100 User-Agent: KNode/4.4.11 Content-Transfer-Encoding: 8Bit Subject: Re: Two versions of code - advantages and differences Newsgroups: comp.lang.javascript References: <1512443.gQHv90F1im@PointedEars.de> <2329900.uRMUlLskrn@PointedEars.de> <1810646.Zi7hGo9um1@PointedEars.de> Followup-To: comp.lang.javascript MIME-Version: 1.0 Lines: 212 NNTP-Posting-Date: 07 Jan 2013 03:58:49 CET NNTP-Posting-Host: 1918f7bf.newsspool1.arcor-online.net X-Trace: DXC=?l>;eVfDFWD[6=1B@oB@@@ic==]BZ:afN4Fo<]lROoRAnkgeX?EC@@@eQ4FNC@f86KDZm8W4\YJNLb@mF9jNikdEWWB88 Thomas 'PointedEars' Lahn wrote: >> David Mark wrote: >> > Thomas 'PointedEars' Lahn wrote: >> >> David Mark wrote: >> >> > Thomas 'PointedEars' Lahn wrote: >> >> >> [You] should not simply return in the second branch in Version 2, >> >> >> [because calls will fail then; you should return a reference to a >> >> >> Function instance with an empty function body. >> >> >> >> My point is that the latter should return a function here as the rest >> >> of the code hinges on the callability of the returned value. And if I >> >> am not very much mistaken, this is the pattern that you are employing >> >> in My Library, too. >> > >> > I think you have that backwards. My Library does leave API properties >> > undefined to indicate that there is no method available. >> >> In that case I must say that I do not think it is wise to leave it to the >> caller to check for the existence of an API feature, and you will observe >> that in JSX I am returning “undefined” or “null”, e. g., my >> getElementById() emulation returns “null” if none of the available >> approaches are supported. > > And if it can't find the element? It also returns null, right? Yes, AISB. > That's a trivial example that demonstrates the problem of trying to slap a > static API on environments that are unknown until run time. I fail to see a problem with that approach. In fact, ISTM the very problem that you perceive in my approach (without it actually being there) occurs with your approach. Apparently you are testing *everything* *too early*. >> That way you can write, for example >> >> … = (function () { >> var _getElementById = jsx.dom.getElementById; >> >> return function () { >> var foo = _getElementById("foo"); >> if (foo) >> { >> /* do something with #foo */ >> } >> }; >> }()) >> >> and if none of the gEBI() approaches is going to work simply nothing will >> be done, the same as if there is no element with that ID. Why bother >> about the difference? > > In this trivial example, you could certainly get away with that. I have yet to see an example where this method does not work. This approach is possible because we can presuppose an initialized “document” property even before the document has been loaded. By contrast, several other JSX methods perform feature tests not before they are called, because doing it differently would mean object inference, which is a bad idea. >> You cannot get to an element object, so leave a possible >> related element as it is, and refrain from doing what depends on the >> existence of the element. > > Yes. > >> > The dynamic nature of the API allows the calling code to make >> > intelligent decisions *before* calling API functions. Sometimes the >> > decision is to leave the document alone. >> A function that does virtually nothing leaves the document alone. > > Then why are you calling it (or creating it in the first place)? ;) If the purpose of the function is to find out whether an element object is accessible in the used runtime environment, then it has to be called, of course. But that does not need to be its only purpose. If the function can be called to find out whether an element object is accessible, it can return a reference to the corresponding element object at the same time. It should not be necessary to test if this wrapper function exists. >> With your approach you are not allowing, you are *forcing* the caller to >> make the decision, by *forcing* them to check whether what is supposed to >> be a method is not a method at all before they call it, in order to avoid >> a TypeError exception to be thrown. > > That TypeError is your friend. Actually, no. There are too many cases in which a TypeError exception may be thrown. > If you forget to do the appropriate > detection *once* at the top of your application: > > if (API.areFeatures('getEBI', 'getEBCN', 'playAudio', 'whatever')) { > // Put application that requires those features here > } ISTM you are confusing two concepts here. One is performing availability tests (here: feature tests). Those are the domain of the service provider (here: the library developer). They belong *in the service* (here: in the library code), and should be hidden from the user (here: the developer using the library). The other is using the service. The purpose of a service should be to make tasks less complicated for the user; in this case, for them to skip the intricacies of feature tests in their code, so that they can concentrate on the business logic. That means that the features that the service provides should *always* be available, and therefore need _not_ be tested for by the user. What requires tests on part of the user then is only that the service (here: the library method) returns a value that is useful for them in their use-case. Somehow I find it hard to believe that someone using a (DOM) library wants to replace (I am oversimplifying the code here for brevity, bear with me) var foo; if (document.getElementById) { foo = document.getElementById("foo"); } else if (document.all) { foo = document.all["foo"]; } else if (document.layers) { foo = document.layers["foo"]; } else { foo = null; } if (foo) { foo.bar = baz; } with var foo; if (D.gEBI) { foo = D.gEBI("foo"); if (foo) { foo.bar = baz; } } Instead, they would probably just want to write var foo = D.gEBI("foo"); if (foo) { foo.bar = baz; } instead. > Then the helpful TypeError will tell you exactly what feature is > missing (assuming you happen to test in at least one lacking > environment). Of course, there's no substitute for doing it right the > first time. Your approach would have virtue if it was generally possible that calling a property that was not callable threw a *specific* user-defined exception. Then you could catch that exception (and no other). >> I do not think that is a terribly good idea. The users of such code are >> paying for a slightly smaller memory footprint with greater runtime for >> checks that have already been done by the library, *each* time they use >> the library feature. > > Not at all. The whole idea is based on one-off detection, just as the > host objects are detected behind the scenes. I suggest you see some of > my examples (or the related Jessie project). It does not make sense to me to test on the top in advance all features that could possibly be used at the bottom, regardless if any or all of them will ever be needed in the actual program flow. This effectively leads to double maintenance. Duplicates are bad[tm]. DRY. One should only test what one is using right before one actually uses it. Not only because an all-in-one test is much more expensive, but also because circumstances can change considerably between the point in time when the feature test occurs and when the feature is used. So the interval between those two points in time should be short. >> Or they would have to cache the test result. > > Absolutely not. Yes, they would if they were to optimize this approach. >> Why would they want to use a library then? Is not a main goal of a >> library to make programs that use it *easier* and *shorter* to write, and >> *less* complex to the developer; to provide *transparent* access to >> native features? > > You bet. See my examples. ;) That is *not* transparent access. Transparent access presupposes availability of the provider, with *all* the services they provide. -- PointedEars