Use symbols in case statements for great justice #5706

Closed
wants to merge 2 commits into from

4 participants

@amiel

This is syntactic sugar for case statements.

 case user
 when ~:admin?
   # Do stuff
 when ~:active?
   # Do stuff
 end

Would be equivalent to:

 if user.admin?
   # Do stuff
 elsif user.active?
   # Do stuff
 end

With the added benefit that if user is a method, it would only get called once, which, in certain cases, is significantly more efficient without adding complexity to the code.

Rationale

The short answer is because it looks nice.

ActiveSupport does a great job of extending ruby with simple conveniences like Symbol#to_proc which has now been merged in to ruby core. I think this could be part of the sugar that makes Ruby sweeter.

Why the ~

I chose the unary method ~ because I don't see it having any other use for Symbol, but it could just as easily be + or -.

My original concept was to override Symbol#===, which allows case to be used like this:

case user
when :root?
  # Do stuff
when :active?
  # Do stuff
end

but I felt that overriding Symbol#=== is too obtrusive and dangerous.

I wanted to use & as it is already accepted syntax for Symbol#to_proc but this is invalid ruby syntax:

case user
when &:active?
  # Do stuff
end

Negation

In offering this support I also thought about supporting negation: amiel@rails:master...case_with_predicates_and_negative

That branch allows for syntax like this as well:

 case user
 when ! :admin?
   # Do stuff
 when ! :active?
   # Do stuff
 end

This pull request does not support negation in this way. I didn't want to override Symbol#!, and I think the # 2 under Implementation Discussions below would be a better way to handle negation.

Implementation discussion

I tried a few different implementation concepts for this. This one is by far the simplest (thanks @emmanuel for pointing me in that direction).

In ruby 1.9, procs respond to === for use in case (see http://www.aimred.com/news/developers/2008/08/14/unlocking_the_power_of_case_equality_proc/)

Here are a couple of other options:

  1. My first concept was to override Symbol#===, which offered the nicest syntax, but I felt was too obtrusive.
  2. In another version, Symbol#~ returns an object that responds to ===, this is also concise, but not as simple as the other two options. However, it offers the most complete support for negation. Here's an example implementation that also supports negation: amiel@rails:master...case_with_predicates_with_object
@tenderlove
Ruby on Rails member

Having so many branches in your code seems like a smell. I'd rather not encourage it.

I am -1, but I am biased against adding more "syntactic sugar", so we probably need more opinions.

@jeremy jeremy was assigned Apr 4, 2012
@bensonk

@tenderlove Are you suggesting that needing to switch as this enables is a smell, or that you don't like the way @amiel implemented it?

@jeremy
Ruby on Rails member

Cool hack, @amiel! I don't think this belongs in Rails, though. Symbol#to_proc arose because it's an extremely common pattern, whereas this is a single niche usage that introduces a whole new syntax to understand.

Definitely worth pursuing in a separate library!

@jeremy jeremy closed this Apr 4, 2012
@amiel

@jeremy, @tenderlove Cool, thanks for reviewing and considering. I understand not wanting to add this, but I thought it'd be worth a shot. 😁

@tenderlove
Ruby on Rails member

@bensonk the need to switch is a smell. I think @amiel's implementation is brilliant!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment