Skip to content
New issue

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

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

Already on GitHub? # to your account

Add proper Object function matches #2057

Merged
merged 1 commit into from
Feb 19, 2015

Conversation

jridgewell
Copy link
Collaborator

This takes _.matches and turns it into a proper Object Function (takes the object as the first parameter). This should satisfy the performance problems seen in jashkenas/backbone#3487.

I think it’s pretty apparent we jumbled the arguments originally. See the need to special case Backbone.Model#matches instead of auto proxying like the other _ methods. We even have to special case Collection#matches so we don’t kill performance doing simple wheres.

The legacy _.matches(obj) predicate matcher has been extracted into _.matcher. _.matches will return the predicate matcher if only one argument is provided. This legacy support should be removed in v2.0.

if (arguments.length < 2) return _.matcher(object);
var keys = _.keys(attrs), length = keys.length;
if (object == null) return !length;
var obj = Object(object);
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: I'm intentionally not assigning to object here to avoid an arguments deopt.

@jdalton
Copy link
Contributor

jdalton commented Feb 19, 2015

lodash has _.isMatch it'd be nice to align to that and avoid overloading _.matches.

@jridgewell
Copy link
Collaborator Author

Sorry, I meant to link #1985 as well.

I overloaded _.matches because the current functionality isn't implied by the name (I think _.matcher fits it better). I'm not tied to it, and I think getting this (or _.isMatch) merged is more important than my dislike of the current name.

This takes `_.matches` and turns it into a proper Object function
(takes the object as the first parameter). The should satisfy the
performance problems seen in
[jashkenas/backbone#3487](jashkenas/backbone#348
7#issuecomment-75001423).

I think it’s pretty apparent we jumbled the arguments originally. See
the need to special case
[`Backbone.Model#matches`](https://github.com/jashkenas/backbone/blob/ma
ster/backbone.js#L390-L393) instead of auto proxying like the other
[`_`
methods](https://github.com/jashkenas/backbone/blob/master/backbone.js#L
669-L680). We even have to special case
[`Collection#matches`](https://github.com/jashkenas/backbone/blob/master
/backbone.js#L917-L924) so we don’t kill performance doing simple
`where`s.

The legacy `_.matches(obj)` predicate matcher has been extracted into
`_.matcher`. `_.matches` will return the predicate matcher if only one
argument is provided. This legacy support should be removed in v2.0.
@jashkenas
Copy link
Owner

Very nice.

_.matches and _.matcher are nice names for it.

jashkenas added a commit that referenced this pull request Feb 19, 2015
Add proper Object function matches
@jashkenas jashkenas merged commit 11f4474 into jashkenas:master Feb 19, 2015
@jdalton
Copy link
Contributor

jdalton commented Feb 19, 2015

Way to be a team player :(

_.matches already exists, it seems odd to change the behavior now. And most methods in Underscore that return a boolean are prefixed with is.

@megawac
Copy link
Collaborator

megawac commented Feb 19, 2015

Yeah, I agree with @jdalton I think it makes sense for this to be matches (old behaviour) and isMatch for bc.

This will actually break some suggestions I've made on SO and the issues as matches is not gaurded.

@arthurvr
Copy link

Way to be a team player.

Ya :(

@jridgewell
Copy link
Collaborator Author

_.matches already exists. It seems odd to change the behavior now.

But it would make the Backbone method ugly. Model#isMatch isn't as nice as Model#matches.

And most methods in Underscore that return a boolean are prefixed with is.

Could be aliased?

This will actually break some suggestions I've made on SO and the issues as matches is not gaurded.

We can add a guard here, and remove that with v2.0 as well.


I just really dislike having _.matches return a function... 😄

@megawac
Copy link
Collaborator

megawac commented Feb 19, 2015

I feel like that is more effort than its worth: I'd say just leave matches be

@jdalton
Copy link
Contributor

jdalton commented Feb 19, 2015

I just really dislike having _.matches return a function... 😄

I've gotten used to it. It makes sense since it produces a function that checks if an object matches the source. This change feels inconsistent since there's .isEqual and not .equals. And again the overloading is gross, sometimes returning a function or a boolean is yuck, @michaelficarra is rolling over in his seat.

@jridgewell
Copy link
Collaborator Author

@megawac: how were you using _.matches as an iteratee? _.filter(arr, _.matches) doesn't make sense.

@jashkenas: Thoughts on overloading?

@megawac
Copy link
Collaborator

megawac commented Feb 19, 2015

@jridgewell it was something like

var obj = {a: 1};
var hasMatch = _.chain([{b: 1}, 'a']).map(_.iteratee).any((matcher) => matcher(obj)).value();

I don't recall exactly what the code was

@jashkenas
Copy link
Owner

_.matches was a crappy name for what it was doing — it shouldn't have been added with that name in the first place. Mea culpa.

_.matches(options, {async: true})    // => true

Reads nicely.

The overloading is certainly gross, but can be removed over time. Leaving the function named poorly would be worse in the long term.

@megawac
Copy link
Collaborator

megawac commented Feb 19, 2015

@jashkenas I disagree, a poorly named function is much better than changing the contract. This will force some users using matches to update their code for no particularly good reason

@jdalton
Copy link
Contributor

jdalton commented Feb 19, 2015

_.matches was a crappy name for what it was doing

Naming is hard and it can be seen to work as both so _.matches is fine as it was before this PR. For the new functionality _.isMatch better follows the naming conventions in Underscore.

_.matches(options, {async: true})    // => true

Reads nicely.

I'd argue that it doesn't read as nicely because devs are used to a different behavior (with a single arg).

_.matches(source);
_.matches(object, source);

Now, sometimes the first _.matches argument is the source and others it's the object which is clunky.
Something like _.isMatch makes it clear you're performing a check and getting a boolean result.

@braddunbar
Copy link
Collaborator

Yea, returning a function or a boolean with an optional first argument, not second, is about an confusing as it gets. If we dislike the name _.matches then let's alias and deprecate it. I don't think a bad name is a good reason to add a breaking change with completely new functionality.

@jashkenas jashkenas mentioned this pull request Feb 19, 2015
@jridgewell
Copy link
Collaborator Author

I don't think a bad name is a good reason to add a breaking change with completely new functionality.

I don't think any breaking change was introduced. @megawac mentioned guarded behavior but you would never use _.matcher as an iteratee, only it's returned method. I also searched his StackOverflow answers (sorry that's kind of stalker-y) and only found one answer pertaining to _.matches, but it isn't affected by this. And if this is really the concern, we can add a guard flag until the legacy support is removed.

The optional first arg is clunky, but it'll go away in the next major (hopefully) release.

@megawac
Copy link
Collaborator

megawac commented Feb 19, 2015

@jridgewell here's a (currently non working) example of something that would break with this

var isBobOrAlice = _.partial(_.anyPass, _.map([{name: 'Bob'}, {name: 'Alice'}], _.matches));
isBobOrAlice({name: 'Bob'})

#2008 (comment)

@jridgewell
Copy link
Collaborator Author

Damn. I'll work a guard PR.

@megawac
Copy link
Collaborator

megawac commented Feb 19, 2015

Yea, returning a function or a boolean with an optional first argument, not second, is about an confusing as it gets. If we dislike the name _.matches then let's alias and deprecate it. I don't think a bad name is a good reason to add a breaking change with completely new functionality.

👍

The current way this is being implemented wastes LOC, simplicity and introduces breaking risks for a name. Just implement _.isMatch instead 😪

@jashkenas
Copy link
Owner

Alright then — isMatch, and matcher, with matches aliased and deprecated, and removed from the docs.

@jridgewell — you got this?

@jridgewell
Copy link
Collaborator Author

Yah, I'll get a new PR tonight.

Can we get a 2.0 milestone with _.isMatch aliased to _.matches (and possibly _.isEqual -> _.equals)?

@jashkenas
Copy link
Owner

Ah, I was thinking about sooner than that ;) I'll get it.

Re: 2.0 — nah. A little naming jiggery-pokery isn't a good enough reason to start rolling that ball down the hill.

jashkenas added a commit that referenced this pull request Feb 19, 2015
@danfinlay
Copy link

Model#isMatch isn't as nice as Model#matches.

How about pick the grammatically correct isMatching?

# for free to join this conversation on GitHub. Already have an account? # to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants