Bug #9397

Lambda#=== raises `ArgumentError` if the lambda accepts 0 args or requires more than 1

Added by Myron Marston over 1 year ago. Updated over 1 year ago.

[ruby-core:<unknown>]
Status:Rejected
Priority:Normal
Assignee:-
ruby -v:1.9+ Backport:1.9.3: UNKNOWN, 2.0.0: UNKNOWN, 2.1: UNKNOWN

Description

Ruby 1.9 introduced === on lambdas/procs so that you can put them in case statements. I'm a fan of this, but there's an unfortunate side effect I've run into recently.

If you have a lambda that accepts 0 args (e.g. lambda { }) or requires more than 1 arg (e.g. lambda { |x, y| }), calling === on it will raise an ArgumentError. I understand why: lambdas are strict about the number of arguments, and === is just an alias of call now. However, this makes things difficult in a gem when you want to use === to match arbitrary objects (since it's the general-purpose protocol ruby provides for that purpose) and there may be lambdas provided by the user. I ran into this in RSpec recently, and my (hacky) solution is to rescue ArgumentError:

https://github.com/rspec/rspec-support/commit/caf76c5de26ea1ca93f09f1097a0092ee4bf828d

It would be nice not to have to do this, and IMO, === on all built-in types should maintain the contract that they can be called with 1 arg and will not raise an error. Consider that ruby's syntax doesn't even allow you to call === with 0 or more than 1 argument unless you resort to hacks like send (e.g. obj.send(:===, x, y, z)). Given that, I think that === on lambdas should return false rather than raising an ArgumentError if the lambda does not support being called with 1 argument.

History

#1 Updated by Nobuyoshi Nakada over 1 year ago

  • Status changed from Open to Rejected

Why lambda?
You can use proc for such purpose.

#2 Updated by Myron Marston over 1 year ago

Why lambda?
You can use proc for such purpose.

Users can use rspec's expectations with any kind of object. We can't arbitrarily restrict it and say, "you can't use lambdas". With some new features we're adding, we're leveraging ruby's === protocol, and we can't control what kind of objects users pass to us.

Why is it desirable for a built-in type to raise ArgumentError for === rather than returning false?

#3 Updated by Jon Rowe over 1 year ago

This seems odd behaviour, given that in normal usage you'd never be able to satisfy the constraint to avoid the ArgumentError, I feel this should return false like a proc would do.

#4 Updated by Benoit Daloze over 1 year ago

Consider that ruby's syntax doesn't even allow you to call === with 0 or more than 1 argument unless you resort to hacks like send

The syntax allows it, you just need a dot:
-> a, b { a + b }.=== 1,2
-> a, b { a + b }.===(1,2)
-> { 42 }.===
-> { 42 }.===()

#5 Updated by Benoit Daloze over 1 year ago

Myron Marston wrote:

Why is it desirable for a built-in type to raise ArgumentError for === rather than returning false?

I guess mostly because it would be hard to debug why you never get in a specific clause of the case.

#6 Updated by Myron Marston over 1 year ago

Benoit Daloze wrote:

Consider that ruby's syntax doesn't even allow you to call === with 0 or more than 1 argument unless you resort to hacks like send

The syntax allows it, you just need a dot:
-> a, b { a + b }.=== 1,2
-> a, b { a + b }.===(1,2)
-> { 42 }.===
-> { 42 }.===()

Interesting. You learn something new everyday :).

I'd still put this in the category of hacks like send, though: the normal, idiomatic way of calling operator methods in ruby is to not use a dot. When was the last time you used a dot to send a message like +, -, ==, ===, *, etc?

Also available in: Atom PDF