Proposal: Default object method

J Decker d3ck0r at gmail.com
Mon Jan 28 12:45:48 UTC 2019


On Sun, Jan 27, 2019 at 10:46 PM Ranando King <kingmph at gmail.com> wrote:

> 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);
>   }
> }
> ```
>

Can't you just use a function object?

function UserCreator(repository) {
     if( !(this instanceof UserCreator ) ) return new
UserCreator(repostiory);
    var creator = function xyz(name) {
         return { repo: repository, user: name }
    }:
    creator.repository = repository;
    creator.create = creator
    return   creator;
}


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

both of the above also work that way.


>
> 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
>>
> _______________________________________________
> 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/ee518c4c/attachment.html>


More information about the es-discuss mailing list