Project

General

Profile

Actions

Feature #17411

closed

Allow expressions in pattern matching

Added by zverok (Victor Shepelev) about 3 years ago. Updated almost 3 years ago.

Status:
Closed
Target version:
-
[ruby-core:101546]

Description

Code:

version = {name: '2.6', released_at: Time.new(2018, 12, 25)}
version in {released_at: Time.new(2010)..Time.new(2020)}
#                            ^ syntax error, unexpected '.', expecting '}'

# This works:
range = Time.new(2010)..Time.new(2020)
version in {released_at: ^range}
#=> true

(Fails with all versions of the pattern matching, in, => and case ... in, and on Ruby 2.7 too.)

Am I missing something about the syntax?..

Actions #1

Updated by marcandre (Marc-Andre Lafortune) about 3 years ago

  • Tracker changed from Bug to Feature
  • Subject changed from Syntax error with . in pattern to Allow expressions in pattern matching
  • Backport deleted (2.5: UNKNOWN, 2.6: UNKNOWN, 2.7: UNKNOWN)

IIUC, it is by design that you can't have expressions in pattern match; pattern match are a quite separate syntax.

I agree that it is quite limiting, so I've taken the liberty to changing your issue to a feature request.

It would be great to allow expressions somehow. Maybe when wrapped in () or ``?

version in {released_at: (Time.new(2010)..Time.new(2020))}
# or
version in {released_at: `Time.new(2010)..Time.new(2020)`}

Updated by matz (Yukihiro Matsumoto) about 3 years ago

@marcandre (Marc-Andre Lafortune) The idea is interesting. I propose ^(expression) notation.

Matz.

Updated by marcandre (Marc-Andre Lafortune) about 3 years ago

That's perfect 👍
Any chance Nobu can come up with a patch quickly?

Updated by matz (Yukihiro Matsumoto) about 3 years ago

Thank you. @nobu (Nobuyoshi Nakada) We need approval from @ktsj (Kazuki Tsujimoto) as well. It will be available in 3.1.

Matz.

Updated by marcandre (Marc-Andre Lafortune) about 3 years ago

  • Assignee set to ktsj (Kazuki Tsujimoto)

Updated by ktsj (Kazuki Tsujimoto) about 3 years ago

I am basically positive.
(I've had the same idea. :) https://speakerdeck.com/k_tsj/pattern-matching-new-feature-in-ruby-2-dot-7?slide=64)

But I've been a little concerned most of languages which have pattern matching don't support such syntax.
If there were a obvious reason, I'd like to know that.

Updated by mame (Yusuke Endoh) about 3 years ago

ktsj (Kazuki Tsujimoto) wrote in #note-7:

If there were a obvious reason, I'd like to know that.

I can think of three reasons.

  • (1) This feature makes an exhaustive check impossible (like a guard), which is not related to Ruby's pattern matching.
  • (2) It is difficult for a compiler to generate efficient code. For example, it cannot create a jump table statically. But we already have a pinning operator, so this may not be related to Ruby.
  • (3) The semantics will become very complicated when the expressions have side-effects.

For (3), I think that the evaluation order should be unspecified; expression patterns may or may not be evaluated at any time (including compilation time). Otherwise, it would be very difficult or even almost impossible to optimize pattern matching. But I'm unsure if "the evaluation order is unspecifed" is acceptable for Ruby.

Updated by marcandre (Marc-Andre Lafortune) about 3 years ago

mame (Yusuke Endoh) wrote in #note-8:

ktsj (Kazuki Tsujimoto) wrote in #note-7:

If there were a obvious reason, I'd like to know that.

I can think of three reasons.

  • (1) This feature makes an exhaustive check impossible (like a guard), which is not related to Ruby's pattern matching.
  • (2) It is difficult for a compiler to generate efficient code. For example, it cannot create a jump table statically. But we already have a pinning operator, so this may not be related to Ruby.
  • (3) The semantics will become very complicated when the expressions have side-effects.

We also already have all theses issues/possibilities (including modifying local variables on the fly):

var = 42
proc = ->(obj) { var = 666; true }
case [:foo, 42]
in [^proc, ^var]
  puts "match"
else
  puts "no match"
end
# => no match (currently)

I don't see an issue with this: don't shoot yourself in the foot...

For (3), I think that the evaluation order should be unspecified; expression patterns may or may not be evaluated at any time (including compilation time). Otherwise, it would be very difficult or even almost impossible to optimize pattern matching. But I'm unsure if "the evaluation order is unspecifed" is acceptable for Ruby.

By "compilation time" you mean "parse time"? That does not seem acceptable; it has to be at runtime.

As for order, we can leave it unspecified, or decide that order should be by priority (say basic patterns first, then pin variable & expressions, and guards last) and left to right within a priority group? It seems that it is what all implementations should prefer doing.

Actions #10

Updated by ktsj (Kazuki Tsujimoto) almost 3 years ago

  • Status changed from Open to Closed

Applied in changeset git|21863470d965b8cc299b1f82417c70d5d26f8ab2.


Pattern matching pin operator against expression [Feature #17411]

This commit is based on the patch by @nobu (Nobuyoshi Nakada).

Actions

Also available in: Atom PDF

Like0
Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0