jsigbiz / spec

JavaScript signature notation
130 stars 6 forks source link

The "this" value #4

Open Skalman opened 11 years ago

Skalman commented 11 years ago

When calling a function there's an extra parameter that you usually don't think about: the this value.

The signature for Array.prototype.join feels a bit off: // ( separator?: String ) => String

I don't have any really good suggestions for what it should look like. Here are a couple, though:

  1. A special parameter name this: // (this: ArrayLike, separator?: String ) => String (might be too magic)
  2. Place the type and a period before: // ArrayLike.( separator?: String ) (not very beautiful)

Btw, is there a way to specify the type ArrayLike with jsig? {length: Number} only reaches part way there. I can't think of a good solution for that without somehow allowing arbitrary JS validation, like:

ArrayLike: {length: Number} where { obj.length === 0 || (0 in obj && (obj.length - 1) in obj) }
junosuarez commented 11 years ago

The this context not currently covered - great catch! Languages like Python consider it the same as a parameter - @kurttheviking, have you seen this case covered well in python, for example?

I'm not very satisfied with either 1 or 2 that you propose. In 1, it distracts from the position in the parameter list, which is actually quite significant. 2 makes is less clear for named function signatures, eg:

String.prototype.toLowerCase (String) => String

A few things come to mind: a # is often used in ad hoc notations to refer to an instance method. Without making assumptions about any particular constructor, some variant might be used to describe the expected this value:

ArrayLike#join( separator?: String ) => String

ArrayLike is hard to specify generically, but it is a common enough case in both ES5 and popular APIs (like the DOM) that it might be good to include it in the jsig spec itself. Again, consider that the aim of jsig is to facilitate communication with other programmers. Without seeing a formal definition, but being familiar with JavaScript, would seeing the term ArrayLike be ambiguous to you?

Skalman commented 11 years ago

I was thinking about characters to use, but didn't think of #. To me your version looks good.

The term ArrayLike is indeed quite clear, but it would be really nice if jsig were parsable. The main goal should be readability for humans, but it would be a pity if it were impossible to create tooling around this.

I agree that ArrayLike type should be in the spec. But I don't like magic that I can't reproduce, so I think there should be a way to define something like ArrayLike.

TypeName: BaseType where { JS expression }
TypeName: BaseType where { JS code that returns something }

This would also allow for something like // Char: String where { obj.length === 1 }

junosuarez commented 11 years ago

If we take constraints, I like the idea of using plain JavaScript expressions - but in this case where is obj coming from in obj.length? Is that a keyword used by convention to refer to the object under constraint?

Raynos commented 11 years ago

For any function that relies on this I've made it obvious that it's a method that relies on this and then have just treated it as the first parameter to a function. For example ( https://github.com/Raynos/immutable-hash#hashgetkey )

Skalman commented 11 years ago

@jden Yeah, obj is just the "in parameter". Would something like val or instance be better? Any ideas?

@Raynos I think that jden's suggested Type# is clearer than Type ->.

junosuarez commented 11 years ago

I'm reluctant to introduce Haskell style typing. I personally quite like it, and it seems very elegant to me. But it hasn't helped me when trying to communicate with non-Haskell people. As much as possible, I'd like to try to stick to JavaScript itself for syntax precedent (or ES6, which has already hashed out a lot of the conversations around evolving the language syntax in a way that will fit in nicely with existing code). Also, in JavaScript, currying is not the default behavior, so expressing N-ary functions as A -> B -> C rather than (A, B) => C doesn't play nicely with the mental model of how functions in JavaScript are executed.

I've made it obvious that it's a method that relies on this

In this example (https://github.com/Raynos/immutable-hash#hashdiffotherhash), you're showing it as parameter named this, which is in line with @Skalman's suggestion above. Expressing it as a first parameter is also similar to how Function#bind, Function#call, and Function#apply take arguments.

Raynos commented 10 years ago

A special parameter name this: // (this: ArrayLike, separator?: String ) => String (might be too magic)

I've implemented this in my Parser ( https://github.com/Raynos/jsig/blob/master/test/parser/functions.js#L101 ).

This approach is encoding that this is just another argument to a function and that if you dont explicitly define this as the first argument then your function doesn't use this.

This implementation is nice because it means there is no extra syntax to express this it's just a function argument label that is interpreted specially, just like how we deal with optional parameters foo : (bar?: T) => void or rest parameters foo : (...bar: X) => void

Feel free to close if this addresses your concerns.

junosuarez commented 10 years ago

I like this idea. Perhaps we should add a section addressing reserved keyword labels. In most cases it makes sense to prohibit them. In this particular case, having a this label in a ParameterList, it makes sense to define the behavior as stipulating the type of the this object in the function scope.

Leaving this open pending clarified text in the readme and or spec.