Project

General

Profile

Feature #17411

Allow expressions in pattern matching

Added by zverok (Victor Shepelev) 3 months ago. Updated 2 months ago.

Status:
Open
Priority:
Normal
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?..

#1

Updated by marcandre (Marc-Andre Lafortune) 3 months ago

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

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) 3 months ago

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

Matz.

Updated by marcandre (Marc-Andre Lafortune) 3 months ago

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

Updated by matz (Yukihiro Matsumoto) 2 months 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) 2 months ago

  • Assignee set to ktsj (Kazuki Tsujimoto)

Updated by ktsj (Kazuki Tsujimoto) 2 months 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) 2 months 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) 2 months 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.

Also available in: Atom PDF