Project

General

Profile

Actions

Feature #19634

open

Pattern matching dynamic key

Added by baweaver (Brandon Weaver) 11 months ago. Updated 10 months ago.

Status:
Open
Assignee:
-
Target version:
-
[ruby-core:113435]

Description

I found myself in a situation (stable marriage problem) where I would like to match against a dynamic key, like so:

mentor_proposals = { mentor_name: ['mentee_1', 'mentee_2'] }
mentor_name = :mentor_name
mentee_name = 'mentee_1'

mentor_proposals in ^key: [*, ^mentee_name, *] # SyntaxError

Currently this is not supported syntax, but there are some use cases in which I might see myself wanting to use it including this one. As deconstruct_keys currently accepts an Array of keys this would not break compatibility but would introduce syntactic complexity in capturing keys on hash-like matches.

I believe the tradeoff is worthwhile, but would like to hear others opinions on the matter.

Granted this case has some oddities of Symbol and String interchangeability as an implementation detail, and I will not be arguing for key irreverence in this issue as that's a much more involved topic.

Updated by matz (Yukihiro Matsumoto) 11 months ago

Probably, you are proposing mentor_proposals in ^mentor_name => [*, ^mentee_name, *]. I still cannot imagine the case where dynamic key is useful.
As far as I know, no other language support dynamic key matching. Please be more concrete to persuade us (preferably, with pseudo code).

Matz.

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

FWIW, Elixir actually supports it, but I don't recall seeing it used in the wild

map = %{mentor_name: "Joe"}
value = "Joe"
key = :mentor_name
match?(%{^key => ^value}, map) # => true

OTOH, Elixir does not support [*, ^mentee_name, *]...

Actions #4

Updated by Anonymous 10 months ago


Updated by austin (Austin Ziegler) 10 months ago

I count 44 instances of this in our production code (~100k lines of Elixir), but I don’t think I’ve ever used key and value pinning as shown in the examples above.

But dynamic key matching is precisely what is required when it is required, although it’s rare enough.

Here's a sample GraphQL response parser or the Shopify Customers API:

  def parse_customer_response(%{} = body, resource, action) do
    gql_action = resource <> String.capitalize(action)

    case body do
      %{status: 200, body: %{^gql_action => %{^resource => %{} = result, "userErrors" => []}}} ->
        {:ok, result}

      %{status: 200, body: %{^gql_action => %{"userErrors" => []} = result}} ->
        {:ok, result}

      %{errors: errors} ->
        {:error, Enum.map(errors, &Map.get(&1, "message"))}

      %{body: %{^gql_action => %{"userErrors" => errors}}} ->
        {:error, Enum.map(errors, &Map.get(&1, "message"))}

      %{body: %{^gql_action => nil}} ->
        {:error, [%{message: "#{gql_action} access denied"}]}

      %{body: nil} ->
        {:error, [%{message: "#{gql_action} access denied"}]}

      _ ->
        {:error, [%{message: "Unknown error"}]}
    end
  end

I haven’t started using pattern matching in Ruby, but I could see some simplified code with dynamic key matching.

(As a separate note, it appears that email replies to redmine aren’t necessarily working, as this should have come in by email.)

Actions

Also available in: Atom PDF

Like0
Like0Like0Like0Like0Like0