Proposal: Default object method

Ranando King kingmph at gmail.com
Mon Jan 28 14:37:33 UTC 2019


Isiah: good find. I was just being sloppy to get the idea across.

J. Decker: The code I gave was just an example of how to do it if it was
for some reason absolutely necessary to use `class`. I don't see the need
and would either tend toward your approach, or just somehow refactor away
the need for the odd construction.

On Mon, Jan 28, 2019 at 6:46 AM J Decker <d3ck0r at gmail.com> wrote:

>
>
> 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/adc1692d/attachment-0001.html>


More information about the es-discuss mailing list