🐥

[bugs.ruby][Feature #20318] パターンマッチに転送引数を渡せるようにする提案

に公開

[Feature #20318] Pattern matching case ... in support for triple-dot arguments

  • case ... in... を渡せるようにする提案
    • ... は転送引数
  • 現状だと以下のように引数を Hash パターンで書いたり
def foo(*args, **kwargs, &block)
  case { args:, kwargs:, block: }
  in args: [name]
    puts name
  in args: [first_name, last_name]
    puts "Hi there #{first_name} #{last_name}"
  in kwargs: {greeting:}
    puts "Hello #{greeting}"
  else
    puts "No match: #{args}"
  end
end

foo "Hi"
foo "Brad", "Gessler"
foo greeting: "Brad"
  • Array パターンで書いたりする
def bar(*args, **kwargs, &block)
  case [args, kwargs, block]
  in [name], {}, nil
    puts name
  in [first_name, last_name], {}, nil
    puts "Hi there #{first_name} #{last_name}"
  in [], {greeting:}, nil
    puts "Hello #{greeting}"
  else
    puts "No match: #{args}, #{kwargs}"
  end
end

bar "Howdy"
bar "Bradley", "Gessler"
bar greeting: "Bradley"
  • これを以下のように転送引数を直接 case に渡せるようにする提案
def foo(...)
  case ...
    in args: [name]
      puts name
    in args: [first_name, last_name]
      puts "Hi there #{first_name} #{last_name}"
    in kwargs: {greeting:}
      puts "Hello #{greeting}"
    else
      puts "No match: #{args}"
  end
end

foo "Hi"
foo "Brad", "Gessler"
foo greeting: "Brad"
  • もしくは [], {}, block で受け取るパターン
def bar(...)
  case ...
  in [name], {}, nil
    puts name
  in [first_name, last_name], {}, nil
    puts "Hi there #{first_name} #{last_name}"
  in [], {greeting:}, nil
    puts "Hello #{greeting}"
  else
    puts "No match: #{args}, #{kwargs}"
  end
end

bar "Howdy"
bar "Bradley", "Gessler"
bar greeting: "Bradley"
def foo(...)
in [name]                                     # foo('ko1')
  puts name
in [first_name, last_name]                    # foo('ko1', 'ssd')
  puts "Hi there #{first_name} #{last_name}"
in {greeting:}                                # foo(greeting: 'hello')
  puts "Hello #{greeting}"
else
  puts "No match: #{args}"
end
  • 上記の書き方は以下のように 1行def で似たような書き方ができるともコメントされていますね
    • これはこれで面白い
def foo(*, **) = case [*, **]
in name, {}
  puts name
in first_name, last_name, {}
  puts "Hi there #{first_name} #{last_name}"
in [{greeting:}]            
  puts "Hello #{greeting}"
in *args
  puts "No match: #{args}"
end

foo('ko1') #=> "ko1"
foo('ko1', 'ssd') #=> "Hi there ko1 ssd"
foo(greeting: 'hello') #=> Hello hello
foo('ko1', 'ssd', greeting: 'hello') #=> No match: ["ko1", "ssd", {:greeting=>"hello"}]
  • 他にも以下のように when で分岐できたり
def foo
when (name)                                     # foo('ko1')
  puts name
when (first_name, last_name)                    # foo('ko1', 'ssd')
  puts "Hi there #{first_name} #{last_name}"
when (greeting:)                                # foo(greeting: 'hello')
  puts "Hello #{greeting}"
else
  puts "No match: #{args}"
end
  • ブロック引数でも利用したいなどのコメントもされています
HTTP.post("http://example.com/some-resource", "some data") do |...|
in code: (200...300)
  puts "Success!"
in code: (300...400), location:
  puts "Redirect to #{location}"
  # post to location
in code: (500...600)
  puts "Server error"
  do_retry
end
  • いずれの書き方も面白いとは思いつつ、現状の構文と比べてできないことができるようになるわけではないのでなんかもっといい感じの拡張機能がほしい
GitHubで編集を提案

Discussion