マネーフォワード社内PRに見られるRubyの書き方について – (6) 受け渡しのパターンマッチング 1

エンジニアの澤田です。

この連載では、社内のRuby (on Rails)コードで気になった箇所の問題点やそこから発展して関連事項を議論しています。

5回目の「マネーフォワード社内PRに見られるRubyの書き方について – (5) 文の環境」では文の環境を考察しました。
今までは1記事1テーマで書いていましたが、毎回長くなりがちだったので、今回からは、1つのテーマでも長くなるときは短く分割し、復数の記事にします。今回から受け渡しのパターンマッチングについて扱います。


【バックナンバー】


オブジェクトを渡して定数や変数で受けるとき、Rubyでは1対1で行うだけでなく、多重代入、分解、統合、不要であることのマーキングなどの多様な機能を使うことが出来ます。これらはまとめて「パターンマッチング」と呼ばれています。
今回の6回目は、このうち、「オブジェクトを渡すときのパターンマッチングの要素」についてまとめます。1

7回目は逆に「定数や変数で受けるときのパターンマッチングの要素」のまとめ、8回目は6, 7回目に関係する見かけたコードの紹介とそれについての議論をします。

オブジェクトを渡すときのパターンマッチングの要素

1. 単一のオブジェクト

リテラルを使うか定数や変数でオブジェクトを表します。連載5回目で扱った文の環境全てに現れます。

{a: 1}
foo

2. 配列リテラルの[]の補完

次の環境で配列リテラルの[]を省略することが出来ます。

  • 制御構造のbreak, next, returnの引数 (連載5回目の⑤a)

    break "foo", "bar"
    
  • 代入式の右辺 (⑤c)

    x = 1, 2
    y, z = 3, 4
    

3. ハッシュリテラルの{}の補完

次の環境でハッシュリテラルの{}を省略することが出来ます。

  • 配列リテラルの最後の要素 (⑤f)

    ["bar", "a" => 1, "b" => 2]
    

また、Ruby 3になるまでは、次の環境でハッシュリテラルの{}を省略することが出来ます。

  • メソッドやsuperyieldの最後の実位置引数 (⑤d, e)

    foo("bar", "a" => 1, b: 2)
    

しかし、この規則により、実引数の最後がハッシュの位置引数なのか、それともキーワード引数があるのかの区別が曖昧であったり複雑であったりしたので、Ruby 3ではこの補完規則は廃止される予定になっています。

4. *fooによる配列の分解

以下に相当する位置:

  1. 制御構造rescue, whenの引数のうちの連続した任意の個数 (= 1個の場合は⑤a)

    rescue AException, *foo, BException
    
  2. メソッドの実位置引数のうちの連続した任意の個数 (= 1個の場合は⑤d, e)

    bar("a", *foo, "b")
    
  3. 配列リテラルの要素のうちの連続した任意の個数 (= 1個の場合は⑤f)

    ["a", *foo, "b"]
    

*をオブジェクトfooの前に付けると、暗黙的にfooのメソッドto_aの呼び出しが試みられ、

  • to_aが未定義なら*foofooに展開され、
  • to_aが配列以外を返せば、タイプエラーが発生し、
  • to_aが配列を返せば、*fooはその配列の要素の並びに展開されます。

5. **fooによるハッシュの分解

以下に相当する位置:

  1. メソッドの実キーワード引数と値の対のうちの連続した任意の個数

    bar(baz, a: "b", **foo, c: "d")
    
  2. ハッシュリテラルのキーと値の対のうちの連続した任意の個数

    {"a" => "b", **foo, "c" => "d"} 
    

**をオブジェクトfooの前に付けると、暗黙的にfooのメソッドto_hashの呼び出しが試みられ、

  • to_hashが未定義ならタイプエラーが発生し、
  • to_hashがハッシュ以外を返せば、タイプエラーが発生し、
  • to_hashがハッシュを返せば、**fooはそのハッシュのキーと値を各各キーワード引数とその値とするものに展開されます。

ただし、Ruby 2.7になるまでは、

  • to_hashがハッシュを返し、それがシンボル以外のキーを持てば、タイプエラーが発生します。

**によるハッシュの分解については、バグっぽい挙動がいくつか知られています。その多くは「ハッシュリテラルの{}の補完」の節で述べた省略規則やキーワード引数とメソッドの最後のハッシュ引数との区別が曖昧であったり複雑であったりすることに起因しています。Ruby 3ではこの省略規則がなくなり、また区別が明確になることで、このような問題がなくなると期待されます。2

6. &fooによるProcオブジェクトの変換

以下に相当する位置:

  1. メソッドの最後の実引数

    bar("baz", &foo)
    

&をオブジェクトfooの前に付けると、暗黙的にfooのメソッドto_procの呼び出しが試みられ、

  • to_procが未定義ならタイプエラーが発生し、
  • to_procProcオブジェクト以外を返せば、タイプエラーが発生し、
  • to_procProcオブジェクトを返せば、&foofooをそのメソッドに付随するコードブロックに展開されます。

まとめ

今回はオブジェクトを受け渡すときのパターンマッチングについて、渡す側の文法的要素についてまとめました。次回のは受け取り側を扱います。

最後に

マネーフォワードでは、エンジニアを募集しています。
ご応募お待ちしています。

【採用サイト】
マネーフォワード採用サイト
Wantedly

【マネーフォワードのプロダクト】
お金の見える化サービス 『マネーフォワード ME』 iPhone,iPad Android

ビジネス向けバックオフィス向け業務効率化ソリューション 『マネーフォワード クラウド』

おつり貯金アプリ 『しらたま』

おトクが飛び出すクーポンサービス 『tock pop』

金融商品の比較・申し込みサイト 『Money Forward Mall』

くらしの経済メディア 『MONEY PLUS』

本業に集中できる新しいオンライン融資サービス 『Money Forward BizAccel』


  1. この記事はRuby開発者の一人である卜部昌平氏に目を通してもらいました。記事に間違いがあれば、それは筆者の責に帰するものです。 ↩︎

  2. https://bugs.ruby-lang.org/issues/14183 参照 ↩︎

Pocket