Proposal: Default object method

Ranando King kingmph at gmail.com
Mon Jan 28 06:46:14 UTC 2019


Jordan's right. This one is best handled by a function. But if there is
some reason you need to create callable objects, it's still doable, even
with ES as-is. Just extend your classes from something like this:

```js
class Callable {
   constructor(defaultFn) {
      return (...args) => { return defaultFn.call(this, ...args); };
   }
}
```
Any class extending this will have instances that are functions. So using
your UserCreator class...

```js
class UserCreator extends Callable {
  constructor(repository) {
    super(this.create);
    this.repository = repository;
  }

  create(name) {
     return this.repository.createUser(name);
  }
}
```

Now `new UserCreator(someRepo)(someName)` is the same as `new
UserCreator(someRepo).create(someName)`.

On Sun, Jan 27, 2019 at 11:35 PM Jordan Harband <ljharb at gmail.com> wrote:

> Something that can be invoked has a `[[Call]]` slot, and is `typeof`
> "function".
>
> Adding a Symbol that makes something callable would have a number of
> effects - it would make `typeof` (one of the most robust operations in the
> language) unsafe, because it would have to access the Symbol method, which
> could be a throwing getter (or even one that just logs how many typeofs are
> called on it). Additionally, it would mean any object could become
> callable, and any function could be made *un* callable.
>
> This seems like a pretty large change, solely to avoid "classes with a
> single method", which arguably should just be a function in the first place.
>
> On Sun, Jan 27, 2019 at 4:05 PM Brasten Sager <brasten at brasten.me> wrote:
>
>> Apologies if this has been raised before. I was unable to locate anything
>> similar.
>>
>> Any thoughts or ideas on this proposal would be appreciated!
>>
>> Original:
>> https://gist.github.com/brasten/f87b9bb470973dd5ee9de0760f1c81c7
>>
>> -Brasten
>>
>>>>
>> # Proposal: Default object method #
>>
>> Objects w/ default method can be invoked like a function.
>>
>> ## Problem ##
>>
>> Objects that are well constrained (single responsibility)
>> can tend to end up with a single method, or at least a single method
>> that is important to most consumers. These methods tend to be named
>> by either verbing the class name (eg. `UserCreator.create()`) or with
>> some generic `handle` / `perform` / `doTheObviousThing`.
>>
>> Whatever the name, downstream consumers of the object end up coupled to
>> two implementation details:
>>
>>    1) this thing-doer is an object and not a function
>>    2) this thing-doer's doing method is called `X`
>>
>> ### Example ###
>>
>> Here we are going to create an object that can be used to
>> create a user later. Note that downstream consumers will only
>> care that this object does one thing: create a user. While it
>> make have other methods eventually for use in some limited
>> contexts, creating a user is its primary (and often sole-)
>> responsibility.
>>
>> ```js
>> class UserCreator {
>>   constructor(repository) {
>>     this.repository = repository;
>>   }
>>
>>   create(name) {
>>      return this.repository.createUser(name);
>>   }
>> }
>>
>> const userCreator = new UserCreator(userRepository);
>> ```
>>
>> At this point, the `userCreator` is just a single-method object.
>> It is useful for injecting into other objects that may need to
>> create a user. But the fact that the `userCreator` is an object
>> with a single useful method is an implementation detail to which
>> consumers become coupled.
>>
>> ```js
>>
>> // Consumer of `userCreator`. Although this could itself be a
>> // good example of a "UserCreator"-like object (due to `.handle()`).
>> //
>> class UserSignupHandler {
>>   constructor(userCreator) {
>>     this.userCreator = userCreator;
>>   }
>>
>>   handle(userName) {
>>     // UserSignupHandler is aware of ".create" when it really doesn't
>> have to be.
>>     //
>>     return this.userCreator.create(userName);
>>   }
>> }
>>
>> const handler = new UserSignupHandler(userCreator);
>> ```
>>
>> Notably, if we were to change the implementation of UserCreator later to
>> be
>> a pure function, we would have to change all consumers of UserCreator when
>> conceptually it shouldn't be needed. There is still a thing-doer that has
>> the same input/output.
>>
>>
>> ## Proposed Solution ##
>>
>> An object instance can have a default method. This would allow an
>> object to be "invoked" exactly like a function, hiding the implementation
>> detail from consumers.
>>
>> Note that there are several ways to define how the default method is
>> determined, and this proposal is less concerned with this aspect than with
>> what it looks like to invoke the object. We will demonstrate an option
>> here,
>> but alternatives are welcome.
>>
>> ```js
>> // This particular implementataion would use a Symbol.
>> //
>>
>> class UserCreator {
>>   constructor(repository) {
>>     this.repository = repository;
>>   }
>>
>>   [Symbol.apply](name) {
>>      return this.repository.createUser(name);
>>   }
>> }
>>
>> const userCreator = new UserCreator(userRepository);
>>
>> class UserSignupHandler {
>>   constructor(userCreator) {
>>     // NOTE: at the consumer, it almost makes more sense to
>>     // name these with action verbs, as is done here.
>>     //
>>     this.createUser = userCreator;
>>   }
>>
>>   handle(userName) {
>>     // UserSignupHandler is no longer coupled to the implementation
>> details it doesn't need.
>>     //
>>     return this.createUser(userName);
>>   }
>> }
>>
>> const handler = new UserSignupHandler(userCreator);
>> ```
>>
>> _______________________________________________
>> es-discuss mailing list
>> es-discuss at mozilla.org
>> https://mail.mozilla.org/listinfo/es-discuss
>>
> _______________________________________________
> es-discuss mailing list
> es-discuss at mozilla.org
> https://mail.mozilla.org/listinfo/es-discuss
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20190128/387d95fd/attachment-0001.html>


More information about the es-discuss mailing list