Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Syntax: special handling of tuple condition in case expression #2258

Merged
merged 2 commits into from
Mar 2, 2016

Conversation

asterite
Copy link
Member

@asterite asterite commented Mar 2, 2016

This is a follow up of #2239

Thanks to suggestions from @kostya and @ysbaddaden, I changed the proposed syntax from this:

case exp1, exp2, ..., expN
when cond1, cond2, ..., condN
  body
end

To this:

case {exp1, exp2, ..., expN}
when {cond1, cond2, ..., condN}
  body
end

As you can see, that syntax is already supported by the current compiler, so the compiler is just updated to rewrite the code in a few special cases. Specifically, if the case condition is a tuple literal, then:

  • If a when condition is a tuple literal (starts with {), then the "implicit object" notation is allowed in its members (for example {.even?, .odd?})
  • If a when condition is a tuple literal, a compile error is issued if its size is not that of the condition (otherwise you are checking a case that will always be false)
  • If a when condition is a tuple literal, the comparison made against the case tuple expression is done in a smarter way, similar to how a regular case is handled. For example, case {x, y}; when {Int32, Float64} is rewritten to if x.is_a?(Int32) && y.is_a?(Float64), allowing type filters to work, something that doesn't work right now (this is the main motivation for adding this funcionality).
  • If a when condition is not a tuple literal, the regular === is used. For this to work, I added Tuple#===, which was missing.

With this, we can finally solve real world problems like fizzbuzz in an elegant way:

100.times do |i|
  case {i % 3, i % 5}
  when {0, 0}
    puts "FizzBuzz"
  when {0, _}
    puts "Fizz"
  when {_, 0}
    puts "Buzz"
  else
    puts i
  end
end

Of, if you want {0, 0} as a constant, you can do it too:

ZEROS = {0, 0}

100.times do |i|
  case {i % 3, i % 5}
  when ZEROS
    puts "FizzBuzz"
  when {0, _}
    puts "Fizz"
  when {_, 0}
    puts "Buzz"
  else
    puts i
  end
end

Another advantage of this syntax is that it allows multiple when conditions, for example:

case {x, y}
when {0, 0}, {1, 1}
  # ...
end

@sdogruyol
Copy link
Member

Wow, this is awesome. Feels like pattern matching here 👍

@ysbaddaden
Copy link
Contributor

Strong 👍

@waterlink
Copy link
Contributor

Definite 👍 from my side. I feel that is very empowering feature.

Best Regards,
Oleksii Fedorov,
Competent Programmer,
TDD, Microservices, Ruby, Clojure, Go and more,
+49 15757 486 476

On Wed, Mar 2, 2016 at 9:37 PM, Julien Portalier notifications@github.com
wrote:

Strong [image: 👍]


Reply to this email directly or view it on GitHub
#2258 (comment)
.

@asterite
Copy link
Member Author

asterite commented Mar 2, 2016

One thing that @bcardiff told me to remark is that this won't work:

foo = {1, 2}
case foo
when {.even?, .odd?} # Syntax error: expected token: .
end 

The special rules only apply if the case expression is a tuple literal.

If it's a tuple literal, the syntax desugaring can be done before semantic analysis. Otherwise, in the case above, the compiler would first need to check if foo is a Tuple of that size and then do the corresponding checks. This is something that could be (more or less easily) done in the future, though. Basically rewrite it to:

if foo.is_a?(Tuple(_, _)) && foo[0].even? && foo[1].odd?
  # ...
end

Problem is, .is_a?(Tuple(_, _)) doesn't compile right now, but should be easily fixable.

Note however, that this does work:

case foo
when {1, 2}
  # ...
end

because nothing special (neither .even? nor _) was used in the tuple.

asterite pushed a commit that referenced this pull request Mar 2, 2016
Syntax: special handling of tuple condition in case expression
@asterite asterite merged commit 30a1bee into master Mar 2, 2016
@asterite asterite deleted the feature/case_tuple_cond branch March 2, 2016 23:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants