Thank you all so much for the feedback! The push-back has given me something to 
more to consider.

I’d like to add a couple points which are hopefully clarifying, but I do 
acknowledge the idea doesn’t seem to be attracting positive interest in its 
current form.

I meant to note in the original gist that this isn’t a unique idea. Scala (and 
possibly others) do something very similar. The `apply` symbol was chosen for 
this example based on the name of the apply method from Scala. However, not 
being a Scala developer myself, I am cautious about accidentally attaching any 
baggage that may exist (unknown to me) in Scala’s solution to this proposal.

To Jordan’s points - I would hope that the proposal could be scoped such that 
the impacts mentioned were not so significant. It certainly wasn’t the intent 
to make functions un-callable, for example. I would need to understand ES 
details a bit better to fully work through the objections raised (though this 
has motivated me to do so!).

The proposal provides a very tactical example and one that I run into all the 
time (but perhaps I’m in a minority there). I should add a more strategic 
motivation to the proposal. The broader intent was to allow even better 
interaction between the functional and OO coding styles are common in ES. A 
function and an object are clearly not interchangeable in every situation. 
However there are many situations where they could be - conceptually. The 
proposal would help facilitate that.

Lastly, regarding other ways to accomplish this, I usually end up expecting a 
function in the consumer. Dependencies then either need to be written as 
functions (usually higher-order functions to account for dependency injection), 
or wrapped by a function when provided to a consumer. So, something like this:

```js
// class-based
const userCreator = new UserCreator(userRepository);

class UserSignupHandler {
  constructor(createUserFn) {
    this.createUser = createUserFn;
  }
  
  handle(userName) {
    return this.createUser(userName);
  }
}

const handler = new UserSignupHandler(
  u => userCreator.create(u)
);
```

This is not too bad, syntactically, and gets ever better if the bind-operator 
proposal goes through. But the frequency with which I find myself doing led to 
this proposal.

I’m happy to hear any additional feedback others may have.


> On Jan 27, 2019, at 11:36 PM, Ron Buckton <ron.buck...@microsoft.com> wrote:
> 
> There’s nothing in that proposal says that an object with a `Symbol.apply` 
> has to have a different `typeof`. It *would* mean that any Call might require 
> additional dispatch which could have performance implications. It could also 
> be an approach to support “callable” classes:
>  
> ```js
> class Foo {
>   constructor() { /* constructor behavior */ }
>   static [Symbol.apply]() { /* call behavior */ }
> }
> ```
>  
> From: es-discuss <es-discuss-boun...@mozilla.org> On Behalf Of Jordan Harband
> Sent: Sunday, January 27, 2019 9:35 PM
> To: Brasten Sager <bras...@brasten.me>
> Cc: es-discuss <es-discuss@mozilla.org>
> Subject: Re: Proposal: Default object method
>  
> 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 <bras...@brasten.me 
> <mailto:bras...@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 
> <https://nam06.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgist.github.com%2Fbrasten%2Ff87b9bb470973dd5ee9de0760f1c81c7&data=02%7C01%7Cron.buckton%40microsoft.com%7C8c644033cebc48cdb58708d684e27a3d%7C72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C636842505603533257&sdata=qHxOcUdMF1i1QPc%2BZKC96Qq4%2BjQY1cgo7GBwDQ5f64Y%3D&reserved=0>
>  
> -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@mozilla.org <mailto:es-discuss@mozilla.org>
> https://mail.mozilla.org/listinfo/es-discuss 
> <https://nam06.safelinks.protection.outlook.com/?url=https%3A%2F%2Fmail.mozilla.org%2Flistinfo%2Fes-discuss&data=02%7C01%7Cron.buckton%40microsoft.com%7C8c644033cebc48cdb58708d684e27a3d%7C72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C636842505603533257&sdata=aka94qZafmZwTNsAjMMC%2FTvTww5P5sn4cSRmVdSe2yk%3D&reserved=0>
_______________________________________________
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss

Reply via email to