[ruby-talk:444825] Pattern matching as String, passing patters to functions

Hi Rubyists,

is this the place to ask beginner questions?

I am confused about pattern matching. It only seems to work when the key is of type symbol.
How would I apply pattern matching to string keys?

Example

require "yaml"

yaml_data="
cards_on_hand:
- suite: hearts
value: '9'
- suite: hearts
value: 'king'
- suite: diamonds
value: 'king'
"

y=YAML.safe_load(yaml_data, symbolize_names: true)
puts y.inspect

y[:cards_on_hand].each do |a_card|
case a_card
in { suite: String => captured_suite, value: 'king' }
puts "you have a king on your hand and its suite is #{captured_suite}"
else
puts "a card of no interest #{a_card.inspect}"
end
end

{cards_on_hand: [{suit: "hearts", value: "9"}, {suit: "hearts", value: "king"}, {suit: "diamonds", value: "king"}]}

a card of no interest {suite: "hearts", value: "9"}
you have a king on your hand and its suite is hearts
you have a king on your hand and its suite is diamonds

Second question:

Since everything in Ruby is an object, how do I pass patterns into a function? Even more specific, how do I handle capture when passing to a function?

Cheers,
Marcus

···

______________________________________________
ruby-talk mailing list -- ruby-talk@ml.ruby-lang.org
To unsubscribe send an email to ruby-talk-leave@ml.ruby-lang.org
ruby-talk info -- https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-talk.ml.ruby-lang.org/

im lazy. straight from copilot :slight_smile:

:key: Pattern Matching with String Keys
By default, Ruby’s hash pattern matching expects symbol keys. That’s why
your example works when you load YAML with symbolize_names: true. If you
keep string keys, you need to explicitly match them:

ruby
y = YAML.safe_load(yaml_data) # no symbolize_names

y["cards_on_hand"].each do |a_card|
  case a_card
  in { "suite" => String => captured_suite, "value" => "king" }
    puts "you have a king on your hand and its suite is #{captured_suite}"
  else
    puts "a card of no interest #{a_card.inspect}"
  end
end
Notice the difference:

suite: → matches a symbol key

"suite" => → matches a string key

So:

Use symbolize_names: true if you want to work with symbols.

Or match with "key" => if you want to keep string keys.

:package: Passing Patterns into Functions
Patterns in Ruby are objects (they’re part of the language syntax). You
can’t directly “store” a pattern as a variable, but you can wrap matching
logic in a method and use case ... in inside it.

Example:

ruby
def match_card(card, pattern)
  case card
  in pattern
    yield $~ if block_given? # or just return true
  else
    nil
  end
end

pattern = { suite: String => suite, value: "king" }

y[:cards_on_hand].each do |card|
  match_card(card, pattern) do
    puts "Captured suite: #{suite}"
  end
end
Another approach is to use procs/lambdas for reusable matching logic:

ruby
king_pattern = ->(card) {
  case card
  in { suite: String => s, value: "king" }
    s
  else
    nil
  end
}

y[:cards_on_hand].each do |card|
  if (suite = king_pattern.call(card))
    puts "You have a king of #{suite}"
  end
end
This way, you can pass around reusable matchers like any other object.

:rocket: Key Takeaways
Use symbol: for symbol keys, "string" => for string keys.

Wrap patterns in methods or lambdas to pass them around.

Captures (=> var) work fine inside those reusable matchers.

···

On Tue, May 26, 2026 at 10:12 PM Marcus Poller via ruby-talk < ruby-talk@ml.ruby-lang.org> wrote:

Hi Rubyists,

is this the place to ask beginner questions?

I am confused about pattern matching. It only seems to work when the key
is of type symbol.
How would I apply pattern matching to string keys?

Example

require "yaml"

yaml_data="
cards_on_hand:
  - suite: hearts
    value: '9'
  - suite: hearts
    value: 'king'
  - suite: diamonds
    value: 'king'
"

y=YAML.safe_load(yaml_data, symbolize_names: true)
puts y.inspect

y[:cards_on_hand].each do |a_card|
   case a_card
   in { suite: String => captured_suite, value: 'king' }
     puts "you have a king on your hand and its suite is #{captured_suite}"
   else
      puts "a card of no interest #{a_card.inspect}"
   end
end

{cards_on_hand: [{suit: "hearts", value: "9"}, {suit: "hearts", value:
"king"}, {suit: "diamonds", value: "king"}]}

a card of no interest {suite: "hearts", value: "9"}
you have a king on your hand and its suite is hearts
you have a king on your hand and its suite is diamonds

Second question:

Since everything in Ruby is an object, how do I pass patterns into a
function? Even more specific, how do I handle capture when passing to a
function?

Cheers,
Marcus
______________________________________________
ruby-talk mailing list -- ruby-talk@ml.ruby-lang.org
To unsubscribe send an email to ruby-talk-leave@ml.ruby-lang.org
ruby-talk info --
https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-talk.ml.ruby-lang.org/

String keys do not work with pattern matching, or more specifically pattern
expressions in this case. The only way to get around that is to use
something to symbolize the keys.

That said, I have argued before that we should treat pattern expression
keys as irrelevant, whether they are Strings or Symbols.

The other option is to use something like Dio to make it irreverent for you:

The general direction of my argument in this regard is that pattern
expressions are exactly that: patterns. They are not Symbols, nor are they
Strings, just as keyword arguments are keyword arguments and not Symbols
either.

Regarding your second point? Patterns are not first-class objects, unlike a
regular expression, for example. However, you can cheat this one a bit more
elegantly:

captured_pattern = -> value { value in pattern: /match/ }

You use a function / proc / lambda / block to do it. That's also something
I want, in addition to composable patterns (e.g., capturing a pattern and
reusing it inside another one as a branch or a part of it.)

···

On Sun, May 31, 2026 at 10:14 PM botp via ruby-talk < ruby-talk@ml.ruby-lang.org> wrote:

im lazy. straight from copilot :slight_smile:

:key: Pattern Matching with String Keys
By default, Ruby’s hash pattern matching expects symbol keys. That’s why
your example works when you load YAML with symbolize_names: true. If you
keep string keys, you need to explicitly match them:

ruby
y = YAML.safe_load(yaml_data) # no symbolize_names

y["cards_on_hand"].each do |a_card|
  case a_card
  in { "suite" => String => captured_suite, "value" => "king" }
    puts "you have a king on your hand and its suite is #{captured_suite}"
  else
    puts "a card of no interest #{a_card.inspect}"
  end
end
Notice the difference:

suite: → matches a symbol key

"suite" => → matches a string key

So:

Use symbolize_names: true if you want to work with symbols.

Or match with "key" => if you want to keep string keys.

:package: Passing Patterns into Functions
Patterns in Ruby are objects (they’re part of the language syntax). You
can’t directly “store” a pattern as a variable, but you can wrap matching
logic in a method and use case ... in inside it.

Example:

ruby
def match_card(card, pattern)
  case card
  in pattern
    yield $~ if block_given? # or just return true
  else
    nil
  end
end

pattern = { suite: String => suite, value: "king" }

y[:cards_on_hand].each do |card|
  match_card(card, pattern) do
    puts "Captured suite: #{suite}"
  end
end
Another approach is to use procs/lambdas for reusable matching logic:

ruby
king_pattern = ->(card) {
  case card
  in { suite: String => s, value: "king" }
    s
  else
    nil
  end
}

y[:cards_on_hand].each do |card|
  if (suite = king_pattern.call(card))
    puts "You have a king of #{suite}"
  end
end
This way, you can pass around reusable matchers like any other object.

:rocket: Key Takeaways
Use symbol: for symbol keys, "string" => for string keys.

Wrap patterns in methods or lambdas to pass them around.

Captures (=> var) work fine inside those reusable matchers.

On Tue, May 26, 2026 at 10:12 PM Marcus Poller via ruby-talk < > ruby-talk@ml.ruby-lang.org> wrote:

Hi Rubyists,

is this the place to ask beginner questions?

I am confused about pattern matching. It only seems to work when the key
is of type symbol.
How would I apply pattern matching to string keys?

Example

require "yaml"

yaml_data="
cards_on_hand:
  - suite: hearts
    value: '9'
  - suite: hearts
    value: 'king'
  - suite: diamonds
    value: 'king'
"

y=YAML.safe_load(yaml_data, symbolize_names: true)
puts y.inspect

y[:cards_on_hand].each do |a_card|
   case a_card
   in { suite: String => captured_suite, value: 'king' }
     puts "you have a king on your hand and its suite is
#{captured_suite}"
   else
      puts "a card of no interest #{a_card.inspect}"
   end
end

{cards_on_hand: [{suit: "hearts", value: "9"}, {suit: "hearts", value:
"king"}, {suit: "diamonds", value: "king"}]}

a card of no interest {suite: "hearts", value: "9"}
you have a king on your hand and its suite is hearts
you have a king on your hand and its suite is diamonds

Second question:

Since everything in Ruby is an object, how do I pass patterns into a
function? Even more specific, how do I handle capture when passing to a
function?

Cheers,
Marcus
______________________________________________
ruby-talk mailing list -- ruby-talk@ml.ruby-lang.org
To unsubscribe send an email to ruby-talk-leave@ml.ruby-lang.org
ruby-talk info --
https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-talk.ml.ruby-lang.org/

______________________________________________
ruby-talk mailing list -- ruby-talk@ml.ruby-lang.org
To unsubscribe send an email to ruby-talk-leave@ml.ruby-lang.org
ruby-talk info --
https://ml.ruby-lang.org/mailman3/postorius/lists/ruby-talk.ml.ruby-lang.org/