From: nick on
On Mar 31, 8:34 pm, David Mark <dmark.cins...(a)gmail.com> wrote:
> nick wrote:
>
> >   Cufon('h1')('h2')('h3');
>
> It's completely ridiculous.  And I mean that in the nicest possible way
> (trying to save you some time here).

Why? It seems pretty self-evident to me. Look at this code:

myApp.keys.register
('w', panNorth)
('s', panSouth)
('a', panWest)
('d', panEast);

// nearby...
function panNorth(evt){/* ... */}
function panSouth(evt){/* ... */}
...

What else could it possibly be doing besides registering a bunch of
key handlers? It works and it makes sense, so it's not entirely
ridiculous. And, really, how is it any worse than something like this:

myApp.keys.register([
['w', panNorth],
['s', panSouth],
['a', panWest],
['d', panEast]
]);

// nearby...
function panNorth(evt){/* ... */}
function panSouth(evt){/* ... */}

This means the register function must be more complex, unless you want
to register a single key handler like this: register([[key,func]])

So maybe there is a bit of overhead to the first method, but if you're
just using it for a one-shot thing like registering key handlers or
replacing fonts it seems to make plenty of sense.
From: Thomas 'PointedEars' Lahn on
nick wrote:

> David Mark wrote:
>> nick wrote:
>> > Cufon('h1')('h2')('h3');
>>
>> It's completely ridiculous. And I mean that in the nicest possible way
>> (trying to save you some time here).
>
> Why? It seems pretty self-evident to me. Look at this code:
>
> myApp.keys.register
> ('w', panNorth)
> ('s', panSouth)
> ('a', panWest)
> ('d', panEast);
>
> // nearby...
> function panNorth(evt){/* ... */}
> function panSouth(evt){/* ... */}
> ...
>
> What else could it possibly be doing besides registering a bunch of
> key handlers? It works and it makes sense, so it's not entirely
> ridiculous. And, really, how is it any worse than something like this:
>
> myApp.keys.register([
> ['w', panNorth],
> ['s', panSouth],
> ['a', panWest],
> ['d', panEast]
> ]);
>
> // nearby...
> function panNorth(evt){/* ... */}
> function panSouth(evt){/* ... */}

1. It requires you to keep an external reference to the initial calling
object as Richard has already explained.

2. Several function calls will be always more expensive than just one and
the creation of an Array instance.

BTW, few would consider spoiling the current execution context's namespace
declaring functions when using Array initializers as anonymous function
expressions would be readily available then.

> This means the register function must be more complex, unless you want
> to register a single key handler like this: register([[key,func]])

With the self-calling approach the register() method must be more complex
instead; it must use a closure to work on the initial calling object when
self-called.

> So maybe there is a bit of overhead to the first method, but if you're
> just using it for a one-shot thing like registering key handlers or
> replacing fonts it seems to make plenty of sense.

You don't know what you are talking about. Incidentally, not even the
crappy jQuery promotes this ridiculous pattern, the chaining there goes
$(...).method(...).method(...) instead (which is only slightly less
inefficient, error-prone and hard to debug).


PointedEars
--
Danny Goodman's books are out of date and teach practices that are
positively harmful for cross-browser scripting.
-- Richard Cornford, cljs, <cife6q$253$1$8300dec7(a)news.demon.co.uk> (2004)
From: Richard Cornford on
On Apr 1, 12:10 am, nick wrote:
> On Mar 31, 7:53 am, Richard Cornford wrote:
>> On Mar 31, 7:10 am, nick wrote:
>> ...
>>> I just discovered a weird side effect that I don't really
>>> understand yet... I wanted a function to be able to chain
>>> itself
>
>> That will not result easily comprehended code. The 'chaining'
>> where an object is returned and methods called on that is poor
>> enough when it comes to intelligibility but at least in that
>> case you have the method names giving slight clues as to the
>> process that is being undertaken. If you (and particularly
>> habitually) start directly calling the return values of
>> function calls in long chains there will be nothing left in
>> the code making the calls that indicates what is going on;
>> it will always be necessary to have familiarity with (all of)
>> the functions being called in order to understand what is
>> going on.
>
> Ah, but isn't this a subjective matter as well?

There are subjective aspects to it (particularly the judgment), but it
is not entirely subjective. Unfortunately the question often gets
decided on a subjective basis; it is easy for me to understand so it
must be easily understandable, where the individual is not considering
what knowledge they are brining to their considerations that others
may not be able to.

The objective assessment would look at (only) the code itself and
asses what could be learnt from that. (Obviously some foreknowledge
must be assumed, like understanding javascript syntax, else attempting
to understand javascript code is not a realistic expectation).

> To me something like
> this is easily understandable, if a bit esoteric:
>
> Cufon('h1')('h2')('h3');

Looking at this code itself we see a call to (what is assumed to be) a
function, and then a call to whatever is returned from that first
call, and then a call to whatever is returned from that. We might also
observe a similarity in the arguments.

That is all the information available in the code. In order to
determine that this actually represents three calls to the same
function it will be necessary to find the code for a - Cufon -
definition, or the code for whatever function is assigned to - Cufon -
at any given time. Otherwise (even if 'Cfon' were instead an
Identifier that described what the function did) the nature/process of
the second and third function calls remains unknown. It is not even
hinted at by the code above, and that is an objective fact.

>> In code authoring there are a range of possibilities when it
>> comes to the comprehensibility of the code, ranging from the
>> self-documenting (where variable/function/method names, and
>> the use of such, pretty much state what is going on at any
>> point) to the utterly obscure (or "elegant" as it is often
>> called these days). Your plan seems to be aiming at the obscure.
>
> To the contrary, my plan aims at brevity

I can see the brevity, but don't regard that as advantageous in
itself.

> and simplicity,

That is a subjective judgment. I would not judge the above simpler
than what follows.

> both in the library itself and in the calling convention.

As a general convention that is a frightening prospect. If you did
this exclusively with functions that returned references to themselves
then familiarity with the "library" might make this acceptable as then
you would be able to assume that - x()()(); - represented a sequence
of calls to - x -. But just one instance of a function that returned
any other function and all instances of code like - x()()(); - start
to need detailed examination before it can be understood.

> While I agree that code like this may be less self-documenting,

I would say approaching non-self-documenting.

> it's also a lot less to type.

Typing is a very overrated issue. Typing is not that difficult (having
done so myself, it only takes an hour a day for a month to learn how
to touch type (60+ words a minute)), and there are numerous tools and
facilities of tools for aiding code entry (and even if some may need a
little setting up, if code writing is your work (or even a significant
pastime) the long term returns justify the initial effort).

However, far more important than any assessment of the actual work
involved in typing is the realisation that entering code is something
you only do once, but reading code (with the intention of
understanding it) is something that accompanies debugging,
maintenance, modification and enhancement, and the appreciation that
it is in maintenance that the majority of the software authoring
expense lies.

This is where it is important to attempt to step away from your own
individual foreknowledge because there is no guarantee that someone
maintaining code is going to have that same knowledge or appreciation
of inner working of your code. You may know that the - Cufon -
function returns a reference to itself, but the later maintainer won't
know that until they have looked at, and understood - Cufon -.

> Plus, as a user of the function, you can always choose not
> to chain it...
>
> Cufon('h1');
> Cufon('h2');
> Cufon('h3');

Looking at this code, with exactly the same (absence of) understanding
of the code that surrounds it, we again see three function calls, but
here we can see good ground for expecting those to be three calls to
the same function. There is information here that is absent from the
previous example.

Now replace - Cufon - with an identifier that makes an unambiguous
statement about what the function does and maybe we don't even need to
go an look at the function code in order to understand what this code
(and the code shrouding it) is trying to do.

<snip>
>>> Anyway, the obvious fix is to avoid 'this' inside of
>>> functions,
>
>> Solving the wrong problem.
>
> I'm not sure what you mean by that.

The problem that you 'fixed' followed from the style you want to apply
in using your code; the chained function calls. The correct problem to
solve might be whatever it is that encourages you to attempt to adopt
divergent programming styles that introduce new problems if adopted.

> It solves the problem I was having, in that it allows my
> script to function as intended. The problem it solved was
> the one I meant to solve, maybe you meant it's the wrong
> solution to the problem? If so, what do you consider the
> right solution to be?

No, I meant solving the wrong problem.

Richard.
From: Dmitry A. Soshnikov on
On Apr 1, 8:39 am, nick <nick...(a)fastmail.fm> wrote:

[...]

>
>   myApp.keys.register
>     ('w', panNorth)
>     ('s', panSouth)
>     ('a', panWest)
>     ('d', panEast);
>

Although it is less efficient by performance than passing an array,
this approach seems to me quite elegant by code reuse.

Dmitry.
From: nick on
On Apr 1, 6:30 am, Thomas 'PointedEars' Lahn <PointedE...(a)web.de>
wrote:
>
> 1. It requires you to keep an external reference to the initial calling
>    object as Richard has already explained.
>

What about arguments.callee? That's good enough for returning a
reference to the same function.

> 2. Several function calls will be always more expensive than just one and
>    the creation of an Array instance.

Granted, but how much more expensive, and what's the tradeoff? If
average joe web designer screws up the first example 25% less than the
second, it might be worth those few extra milliseconds per page load.

>
> BTW, few would consider spoiling the current execution context's namespace
> declaring functions when using Array initializers as anonymous function
> expressions would be readily available then.
>

What?

> > This means the register function must be more complex, unless you want
> > to register a single key handler like this: register([[key,func]])
>
> With the self-calling approach the register() method must be more complex
> instead; it must use a closure to work on the initial calling object when
> self-called.
>

this.register = function (key, onPress, onRelease) {
if (!initted) init();
if (!onPress && !onRelease) me.unregister(key);
callbacks[toCharCode(key)] = [onPress, onRelease];
return arguments.caller;
}

// or //

this.register = function (key, onPress, onRelease) {
if (!initted) init();
if (typeof key == 'array') {
for (int i=key.length, i--;)
(callbacks[toCharCode(key[i][0])] = [key[i][1], key[i][2]])
}
else {
if (!onPress && !onRelease) me.unregister(key);
callbacks[toCharCode(key)] = [onPress, onRelease];
return arguments.caller;
}
}

....?

> > So maybe there is a bit of overhead to the first method, but if you're
> > just using it for a one-shot thing like registering key handlers or
> > replacing fonts it seems to make plenty of sense.
>
> You don't know what you are talking about.  Incidentally, not even the
> crappy jQuery promotes this ridiculous pattern, the chaining there goes
> $(...).method(...).method(...) instead (which is only slightly less
> inefficient, error-prone and hard to debug).

I don't know what you are talking about :)