It's not clear to me whether a pattern will match only own properties or properties found along the prototype chain.
For example, is the following match expression true or false?
match(Object.create({p: 0})) {
{p}: true,
else: false
}
Bonus question: Would { length } match an array, or do only array patterns match arrays?
@samwgoldman I don't think it would make sense for { length } to match arrays therefore I would guess it is safe to assume the proto wouldn't match. Definitely need a 2nd opinion though.
EDIT: You also need to consider the difference between enumerable and hasOwnProperty discussed below.
@limeblack but length is an own property of arrays!
[].hasOwnProperty("length") === true
I think either all properties should match (lots of builtins have getters on the prototype; these need to be matchable), or, we need a syntactical way to differentiate "own" versus "inherited" (which doesn't currently exist in the language).
I'd probably expect it to be equivalent to an in check, however I'm not sure whether it makes sense or not to consider inherited non-enumerable properties as it could be a hazard to adding new methods to types if they could accidentally wind up in pattern matching.
e.g.:
// I'd expect this to be true
match (Object.create({ x: 10 })) {
{ x }: true,
else: false,
}
// But not this
match ([]) {
{ map }: true,
}
// Of course with Symbol.matches you can always check for anything
const mappable = {
[Symbol.matches](item) {
return typeof item.map === 'function'
}
}
// So it wouldn't prevent custom solutions for those times where
// you do want to check for methods
match ([]) {
mappable: true,
}
@samwgoldman Good catch. length is both not enumerable and a hasOwnProperty Let me clarify.
I don't think it would make sense for nonenumerable properties to match which is how many proto properties exist.
[].propertyIsEnumerable('length') === false
If you would like them to match by default I would then propose something like this.
let length = vector => match (vector) {
{ x!, y!, z! }: Math.sqrt(x ** 2 + y ** 2 + z ** 2), // Matches only enumerable properties
{ x!, y! }: Math.sqrt(x ** 2 + y ** 2), // Matches only enumerable properties
}
@Jamesernator
I like your syntax but wouldn't it be a lot simpler to simpler and cleaner to do something like the following.
var mappable = (item) => {
return typeof item.map === 'function'
}
}
// So it wouldn't prevent custom solutions for those times where
// you do want to check for methods
match ([]) {
mappable? // question mark denotes function checking similar to pattern matching in some languages
}
// then you could even do something like this even well maybe
match ([]) {
((i)=>{true})? // question mark denotes function checking similar to pattern matching in some languages
}
I don't think this proposal can add new syntax that differentiates own and inherited, without first adding that syntax in a general manner.
Note that I fully expect the answer to be "own and enumerable properties only" for the same reasons as described here: https://github.com/tc39/proposal-object-rest-spread/blob/master/Issues.md#own-properties
I just want the spec to clarify this fact ;)
object-rest-spread is a different proposal - and it's aiming to be like Object.assign.
I feel very strongly that this proposal should include all properties, inherited and non-enumerable ones too, including getters.
One use case: I should be able to write a match statement for an arraylike - which works because length is an own property on arrays, but arraylike-ness is not determined by own property, it's determined by [[Get]] - which considers inheritance. So, Object.create({ length: 1 }) is absolutely an arraylike object according to, say, Array.from (and many others) - but if this proposal only considers own properties, then it will be impossible to match arraylikes. To me, that's a dealbreaker.
I don't disagree with what you wrote. While I expect own+enumerable, I'm not particularly a fan of own-sensitive APIs.
However, couldn't you match Array.from(arraylike) { ... } if you wanted to support matching arraylikes?
@ljharb It won't be impossible to match array-likes without matching non-enumerable by default e.g. you'll always be able to use [Symbol.match] in other cases.
const arraylike = {
[Symbol.matches](value) {
return typeof value.length === 'number'
}
}
match ({ length: 1, 0: 10 }) {
arraylike -> val : Array.from(val)
}
I definitely think inherited ones should be included as enumerability whether inherited or not implies this object has this property so the consumer should consider it.
Enumerability is the real case I'm wondering about like it could be a refactoring hazard to ever add a method to something as it could be part of a crude pattern match e.g.:
// Suppose TypeA doesn't have a [Symbol.match] and/or instanceof checks
// are unreliable as the type might be being used cross-realm or something
class TypeA {
someMethod() {
}
}
class TypeB {
fizzbuzz() {
}
}
const type = match (type) {
{ someMethod } : 'Is a TypeA',
{ fizzbuzz } : 'Is a TypeB',
}
// TypeB can't now add a someMethod method as it could break
// any TypeA detection that depends on detecting methods like this
Of course people can always get around it to check for non-enumerable properties but I feel like including non-enumerable properties will just cause pain points with people misusing it.
Hey y'all! #65 has gotten merged, and a lot of issues have become irrelevant or significantly changed in context. Because of the magnitude of changes and subtle differences in things that seem similar, we've decided to just nuke all existing issues so we can start fresh. Thank you so much for the contributions and discussions and feel free to create new issues if something seems to still be relevant, and link to the original, related issue so we can have a paper trail (but have the benefit of that clean slate anyway).
Most helpful comment
object-rest-spread is a different proposal - and it's aiming to be like Object.assign.
I feel very strongly that this proposal should include all properties, inherited and non-enumerable ones too, including getters.
One use case: I should be able to write a match statement for an arraylike - which works because
lengthis an own property on arrays, but arraylike-ness is not determined by own property, it's determined by [[Get]] - which considers inheritance. So,Object.create({ length: 1 })is absolutely an arraylike object according to, say, Array.from (and many others) - but if this proposal only considers own properties, then it will be impossible to match arraylikes. To me, that's a dealbreaker.