Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

definition of "method" #2846

Open
jmdyck opened this issue Aug 5, 2022 · 23 comments
Open

definition of "method" #2846

jmdyck opened this issue Aug 5, 2022 · 23 comments

Comments

@jmdyck
Copy link
Collaborator

jmdyck commented Aug 5, 2022

4.4.37 defines "method" as "function that is the value of a property", which includes just about every intrinsic function. So that definition should probably change.

Originally posted by @jmdyck in #2576 (comment)

We could say "function that is the value of a property of a prototype object". That wouldn't handle [Typed]Array.{from,of}, which don't satisfy that definition, but are described as methods. Still, it's pretty close.

@ljharb
Copy link
Member

ljharb commented Aug 5, 2022

I’d say a method is any function-valued property that pays attention to its receiver, whether static or instance.

@jmdyck
Copy link
Collaborator Author

jmdyck commented Aug 5, 2022

Could you rephrase that in terms that the spec defines?

@ljharb
Copy link
Member

ljharb commented Aug 5, 2022

Which term there does the spec not define? receiver is “this value”, static means “on a constructor”, instance means “on a prototype object”.

@jmdyck
Copy link
Collaborator Author

jmdyck commented Aug 5, 2022

Which term there does the spec not define?

It doesn't define:

  • "function-valued",
  • "pays attention to",
  • "receiver",
  • "static" (except in the phrase "static semantics")
  • "instance" (except that a Parse Node is an instance of a symbol).

receiver is “this value”, static means “on a constructor”, instance means “on a prototype object”.

Okay, so why not use those replacements/expansions instead?

@ljharb
Copy link
Member

ljharb commented Aug 5, 2022

Sure:

A method is a function stored in any object property whose behavior changes based on its this value.

@jmdyck
Copy link
Collaborator Author

jmdyck commented Aug 5, 2022

Much better.

The "stored in" phrase is unusual for the spec. The reader would probably understand that the function in question can be the [[Value]] of a data property, but it's unclear if it can be the [[Get]] or [[Set]] of an accessor property.

In practice, it looks like the spec never refers to accessor functions as "methods". (And that's not due to a #2592 normalization, it goes way back.) I'm guessing that's intentional rather than an oversight, so I'd suggest changing:

a function stored in any object property

to something like:

a function that is the value of a data property

Also, I'd suggest replacing:

behavior changes based on its this value

to:

behavior depends on its this value

(My thinking is that "behavior" refers to the totality of a function's input/output effect, which doesn't change.)

So that would be:
[A method is a] function that is the value of a data property whose behavior depends on its this value

(Either way, it's a bit odd that the "whose" seems to attach to "property", whereas it actually attaches to "function". But I don't think that requires fixing.)

@ljharb
Copy link
Member

ljharb commented Aug 6, 2022

Sounds good; your corrections still read clearly to me!

@ljharb ljharb closed this as completed Aug 6, 2022
@ljharb ljharb reopened this Aug 6, 2022
@raulsebastianmihaila
Copy link

I think you're overcomplicating things. If a child asks their parent what 'rain' is, the parent may say that it's 'fluid water that falls from the clouds'. While rain is water, an important aspect is the relationship between that water and the clouds. Whether a function depends on its receiver is irrelevant when speaking about methods. I remember that in the past you wouldn't be able to call console.log without the console receiver. This changed in the mean time, but the effect of the log function printing something to the console is the same. We would still say that it's a method of the console object because of the way we access it, meaning because of its relationship with object console. The relationship with objects is not intrinsic to the log function so it's not always relevant, we may put the log function it in a variable and refer to it as the 'log function'. But we speak of 'methods' when we consider them in certain relations to objects, for instance when we access the log function through a [[Get]] access on the console object.

Regarding accessor properties, I think that's also irrelevant, because from a property relationship perspective the distinction is only relevant in terms of the property attributes. In the context of methods (when we consider the relationship between objects and functions), data and accessor properties are the same with respect to the result of the [[Get]], [[HasProperty]] and [[OwnPropertyKeys]] operations. Going into more details and distinguishing between properties based on [[GetOwnProperty]], for instance, only overcomplicates things. I like how simply the spec currently defines methods and I think the best would be to leave it as is.

@ljharb
Copy link
Member

ljharb commented Aug 26, 2022

In the console example, console.log isn’t a method anymore - but it used to be. Now it’s just a function in a namespace.

@mhofman
Copy link
Member

mhofman commented Aug 26, 2022

Wondering if it'd complicate definitions to refer to things like console.log as a bound method? When you consider Node.js where you can create multiple Console instances, log is clearly a method related to that specific console instance, but it's usable as a function (without a receiver) because it's bound.

@raulsebastianmihaila
Copy link

raulsebastianmihaila commented Aug 26, 2022

In the console example, console.log isn’t a method anymore - but it used to be. Now it’s just a function in a namespace.

You're using your own discretionary definition of the word 'method' in contradiction with the spec.

@ljharb
Copy link
Member

ljharb commented Aug 26, 2022

@raulsebastianmihaila the spec's definition isn't what's actually important tho - the colloquial one is, and it largely matches the definition I've provided.

@raulsebastianmihaila
Copy link

One traditional and very familiar to experienced JS programmers is this style of creating objects:

function Car() {
  let speed = 0;
  let accelerationRate = 5;

  return {
    get speed() {
      return speed;
    },

    accelerate() {
      speed += accelerationRate;
    },

    decelerate() {
      speed -= accelerationRate;
    }
  };
}

const car = new Car();

car.accelerate();
car.accelerate();
car.accelerate();
car.decelerate();

console.log(car.speed);

Colloquially, and also officially, accelerate and decelerate are methods and they don't use any receiver.

@ljharb
Copy link
Member

ljharb commented Aug 26, 2022

To be fair tho, that module pattern was popular so long ago that there is an entire cohort of very experienced JS developers that have never used it.

In this case I'd actually say they aren't methods - specifically, they can be destructured off and called bare, and they'll still work. They're just pretending to be methods, but they're really just namespaced functions.

@raulsebastianmihaila
Copy link

That's very relative, for instance I wouldn't be surprised if those very experienced JS developers you speak of have never opened the spec. Anyway, would you agree that when that pattern was popular, accelerate and decelerate were considered methods and since then you decided on your own to change the meaning of the word 'method'? If you take a look at some JS books from that 'era' (because, you see, it was so long ago...) you would also find in that pattern the notion of 'private methods', meaning functions defined in the context of that constructor which are not exposed publicly. So, be sure you're contradicting existing books on JS patterns.

As to destructuring, there's an obvious reason not to do that:

const car1 = new Car();
const car2 = new Car();

car1.accelerate();
car2.accelerate();

is much better than

const {accelerate: accelerate1} = new Car();
const {accelerate: accelerate2} = new Car();

accelerate1();
accelerate2();

@ljharb
Copy link
Member

ljharb commented Aug 26, 2022

Sure. That doesn't mean the spec needs to adhere to a definition that includes an obsolete practice, though.

@mhofman
Copy link
Member

mhofman commented Aug 26, 2022

I would consider accelerate and decelerate to be bound methods (by way of the object as closure pattern). They apply to the car instance but can be used as free functions.

And I wouldn't consider that pattern as obsolete (however we prefer to name these functions "makers", and avoid calling them with new)

@raulsebastianmihaila
Copy link

That doesn't mean the spec needs to adhere to a definition that includes an obsolete practice

I don't agree that it's obsolete. It has advantages over post-ES5 classes for instance:

  • your program doesn't break if your method happens to be called without the receiver
  • your program doesn't break if you use private state that you use in your methods and you wrap your objects with proxies and call those methods with the proxies as the receivers, which would break when using classes with private fields

@raulsebastianmihaila
Copy link

we prefer to name these functions "makers", and avoid calling them with new

Is this the position of TC39?

@ljharb
Copy link
Member

ljharb commented Aug 26, 2022

However, your "methods" aren't shared between instances - you have N copies of each function when you have N instances. That pattern was a hack around not having a good declarative way to create a constructor and prototype - class exists now, which makes it (7+ years now) obsolete even if it still has advantages.

@mhofman
Copy link
Member

mhofman commented Aug 26, 2022

Is this the position of TC39?

Sorry for the confusion, no. This is our coding style at Agoric.

@raulsebastianmihaila
Copy link

you have N copies of each function when you have N instances

You can say it's not a perfect pattern, however nowadays people go even further and they create N copies of functions with React hooks on each rerender. I think the sense in which you use the word 'obsolete' is more like 'not trendy', which I think is a strange way of looking at things, given that the pattern has important semantic advantages.

@zakarialaoui10
Copy link

is a property containing a function

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants