Re: A proposal for parameter style

On Apr 28, 2010, at 12:04 , Anselm R Garbe wrote:
>> WDYT?
> 
> I agree with Andrei. Can't see why your current situation is more complex.

Throughout the stack there is greater variety than if we were considering the design of a single API (just because each API is independent doesn't mean we shouldn't look at the ecosystem, and more specifically at how hard it would be for someone to learn all of them).

First, we have a number of such calls that only require the callbacks (e.g. DeviceFilesystem::filesystems). That's simple, you just have:

#0
  PendingOp foo (FooCB successCB, optional ErrCB errorCB);

Now you introduce required parameters (e.g. LocalFilesystem::requestTemporaryFilesystem). There are essentially three ways you can place them:

#1
  PendingOp foo (DOMString foo, FooCB successCB, optional ErrCB errorCB);

This loses the habit from #0 where you always start with the callbacks but it avoids the issues with the other three here. To address that we could frontload all parameters (so that the two callbacks are always last).

#2
  PendingOp foo (FooCB successCB, optional ErrCB errorCB, DOMString foo);

This maintains the "callbacks first" approach but since the error callback is option, the required string foo is (as per WebIDL) optional too. We can enforce it back in prose, but that means that the common case becomes obj.foo(sucCB, null, "foo") which is very annoying.

#3
  PendingOp foo (FooCB successCB, DOMString foo, optional ErrCB errorCB);

This maintains the required factor, but breaks up the callbacks.


The alternative is to put anything that isn't a required parameter into an options object. The same positional permutations are possible, and this is what happens:

#1 works for methods for which you have a required option, but for those that only have optional options you end up with obj.foo(null, sucCB) or obj.foo({}, sucCB). Neither is nice.

#2 and #3 have the exact same problems as above.


We then to other alternatives. One options is to realise that since the error callback is optional, it should go into the options such that we have:

  PendingOp foo (FooCB successCB, optional FooOptions options);

This can be used consistently for all of our APIs, but it has the downside that this looks weird: obj.foo(sucCB, { error: errCB}).

And then there's overloading:

  PendingOp foo (FooCB successCB, FooOptions options);
  PendingOp foo (FooCB successCB, optional ErrCB errorCB, optional FooOptions options);

But we'd have to put that everywhere, incurring lesser readability and more testing.

Finally there's the do-nothing option (which is what we have today) whereby each asynchronous call does it its own way. This has the downside that you always have to look up the documentation to know which variant to use. I know people will write wrappers for this stuff, but it doesn't mean that we *have* to make it hard to use.

In contrast, for the object literal approach:

  - consistency: check
  - easy to memorise: check
  - issues listed above: none

:)

--
Robin Berjon
  robineko — hired gun, higher standards
  http://robineko.com/

Received on Wednesday, 28 April 2010 12:56:06 UTC