この関数を使われるんですか? はい、それでしたらまず関数利用申込書を記入いただいて……

世の中には、軽々に使ってほしくないメソッドというものがありますよね。通常のフローと異なる経路でデータを削除するメソッドとか。 手っ取り早くdeprecatedにしてしまえればいいんですけれど、何かしら理由があれば許可し続けたいものもあるんですよね。

同僚がこういうことで困っていて、ジャストアイディアを口にしたら参考になったと喜んで貰えたのでメモっておきます。

TL,DR;

  # 緊急時に通常のバリデーション等を無視して削除するメソッド
  # 理由なしに緊急削除をしようとしたら実行時エラー
  #
  # 引数なしにおける関数定義
  # 引数がなければ実行時エラーする
  @spec emergency_delete :: none
  def emergency_delete do
    "Do not use this without a reason!" |> raise
  end

  # 理由があるなら許すよ
  # でも空文字列は許さん
  @spec emergency_delete(String.t()) :: :ok
  def emergency_delete(reason) when is_bitstring(reason) and byte_size(reason) > 0 do
    "Deletion completed without any validation!"
    |> IO.puts
  end

思いついた背景

紙の書類などの文化で、「例外的な手続きをするので書類のここに理由を書いてね」みたいなものがよくある気がする。これをコードで素直に表現してしまえ。

コメントと比べて何が嬉しいの?

  • コメントを見ずにうっかり使った人を確実に弾ける
  • コードレビューする側としても、呼び出し先のコメントを見に行くより気付きやすい
  • lintツールが十分整備されていない状況でも使える
  • 型に依存しないので、動的型付け言語でも使える

課題

  • 実行時エラーなのがイマイチ。コンパイルエラーやlintなどで落としたいですよね
  • コードの中に長い自然言語が現れるので頻出するとノイジーかも

(おまけ)なんでテストコードがElixirなの?

同僚がElixirわからんと言っていたので、ついでに話題にしたかった。一旦どの辺がわからないのか、などの叩き台にしたいので、自分が特徴的だと思う部分を雑に話題にしておく。Supervisorがどうのという話もあるけれど、今は一旦構文の話をしておく。

  • |>はパイプライン演算子で、左辺の評価結果を右辺にある関数の第一引数に代入する
  • 同じ名前の関数を複数定義した場合は上から順にパターンマッチを試していって、マッチした関数が適用される
    • パターンマッチは引数の数意外にも細々条件が付けられる
  • 何一つマッチしなければFunctionClauseErrorが発生する
  • Erlangのモジュールはセミコロンを先頭に付けた形で呼び出す(例: :rand.uniform(n))
  • @spec... で型を註釈することで静的解析してくれる