Project

General

Profile

Actions

Feature #17355

open

Using same set of names in or-patterns (pattern matching with Foo(x) | Bar(x))

Added by decuplet (Nikita Shilnikov) about 1 year ago. Updated 3 months ago.

Status:
Assigned
Priority:
Normal
Target version:
-
[ruby-core:101143]

Description

Given pattern matching is officially supported in Ruby 3, I have an idea about making it more flexible.

Currently, this piece of code produces a syntax error

case [1, 2]
in [1, a] | [a, 3] => a then a
end # duplicated variable name

Duplications don't seem to be a problem here, semantically-wise. We just need to check if all patterns have the same set of names. It's supported in OCaml (also here's an RFC in Rust https://github.com/rust-lang/rust/issues/54883) so I think it can work in Ruby too.

I've been using pattern matching in Ruby since day 1 and it worked great so far. Since I use OCaml daily too I miss this feature every once in a while :)
A more practical example: imagine you have code like this

def user_email(user)
  case user
  in User(email:) then email
  in Admin(email:) then email
  in Moderator(email:) then email
  end
end

Clearly, it could be simplified if or-patterns were supported:

def user_email(user)
  case user
  in User(email:) | Admin(email:) | Moderator(email:) then email
  end
end

I'd like to know ktsj (Kazuki Tsujimoto)'s thoughts on this.

Updated by Dan0042 (Daniel DeLorme) about 1 year ago

Or-patterns are supported, just not with variable assignment. I agree with the request but the title of the ticket is a bit misleading.

But I think the user_email example actually makes a rather good case for the usefulness of And-patterns:

def user_email(user)
  case user
  in (User | Admin | Moderator) & {email:} then email
  end
end

Updated by decuplet (Nikita Shilnikov) about 1 year ago

  • Subject changed from Or-patterns (pattern matching like Foo(x) | Bar(x)) to Using same set of names in or-patterns (pattern matching with Foo(x) | Bar(x))

Dan0042 (Daniel DeLorme) wrote in #note-1:

Or-patterns are supported, just not with variable assignment. I agree with the request but the title of the ticket is a bit misleading.

Yeah, thanks, I updated the title.

Dan0042 (Daniel DeLorme) wrote in #note-1:

But I think the user_email example actually makes a rather good case for the usefulness of And-patterns:

def user_email(user)
  case user
  in (User | Admin | Moderator) & {email:} then email
  end
end

Out of curiosity, did you see an example of syntax like that in any other language?

Updated by Dan0042 (Daniel DeLorme) about 1 year ago

decuplet (Nikita Shilnikov) wrote in #note-2:

Out of curiosity, did you see an example of syntax like that in any other language?

No I haven't. I briefly mentioned it in #16464#note-2; it just seemed a logical complement to Or-patterns (pun not intended)

Updated by ktsj (Kazuki Tsujimoto) 12 months ago

  • Assignee set to ktsj (Kazuki Tsujimoto)
  • Status changed from Open to Assigned

I am pretty positive. I will discuss it with Matz after I finish to implement.

Updated by decuplet (Nikita Shilnikov) 3 months ago

It'd be nice to have this in 3.1 (no pushing).

Updated by duerst (Martin Dürst) 3 months ago

I'm confused by this code:

case [1, 2]
in [1, a] | [a, 3] => a then a
end # duplicated variable name

The variable isn't used twice, it's used four times. We can ignore the last one, because this is just to give a result back, and of course we should be able to give a back if we matched it.

But the third a is very confusing. We match a to 2 because [1, 2] matches [1, a] and not [a, 2], but then we match [1, a] again to a? It looks like an attempt to some kind of recursive unification, which is definitely not possible in Ruby.

So I would be okay to allow

case [1, 2]
in [1, a] | [a, 3] => b then a
end # duplicated variable name

or of course also

case [1, 2]
in [1, a] | [a, 3] => b then b
end # duplicated variable name

but anything that comes close to

in [1, a] | [a, 3] => a

seems confusing and dangerous.

Updated by decuplet (Nikita Shilnikov) 3 months ago

I'm confused by this code:

Actually, I'm too! The goal of this request doesn't go beyond having support for

case [1, 2]
in [1, a] | [a, 3] then a
end

The => a is probably a leftover, I don't recall the reason I put it there, thank you for highlighting it.

Actions

Also available in: Atom PDF