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) 10 months ago. Updated 5 days 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) 10 months 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) 10 months 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) 10 months 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) 9 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) 6 days ago

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

Updated by duerst (Martin Dürst) 5 days 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) 5 days 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