パターンマッチの構文が前置か後置か覚えられない人がやりがちなこと

突然思い出して随分前のネタを引っ張り出してきました.

OCamlScala のパターンマッチ構文は共に match というキーワードを使いますが, 前置 / 後置が異なるため, 交互に書いていると混乱して結構な頻度で間違えます.

let y = match x with
  | Some n -> n
  | None   -> 0
val y = x match {
  case Some(n) => n
  case None    => 0
}

あとごく稀に先に case 書いてしまったりする. これは完全に余談です.

y = case x of
  Just n -> n
  Nothing -> 0

話を戻して, 構文間違えて途中で気づいて書き直したりコンパイラくんに怒られるのも嫌なので, Atom の init script に以下のような感じのを書いて, Scala を書いているときに前置で match ... with と書くと勝手に後置に変換してくれるようにしています. case もよく書き忘れるのでついでに補完します.

"use babel";

import { CompositeDisposable } from "atom";

const matchWithRegex = /match(\W+.+?\W+)with/g;

atom.workspace.observeTextEditors(editor => {
  const subscriptions = new CompositeDisposable();
  subscriptions.add(editor.onDidDestroy(() => {
    subscriptions.dispose();
  }));
  subscriptions.add(editor.onDidInsertText(() => {
    const pos = editor.getCursorBufferPosition();
    const scopes = editor.scopeDescriptorForBufferPosition(pos).getScopesArray();
    if (!scopes.includes("source.scala") || scopes.findIndex(scope => /string|comment/.test(scope)) >= 0) {
      return;
    }
    const scanRange = [[pos.row, 0], pos];
    editor.backwardsScanInBufferRange(matchWithRegex, scanRange, ({ match, range, stop, replace }) => {
      stop();
      const prevChar = editor.getTextInBufferRange([range.start.translate([0, -1]), range.start]);
      if (!range.end.isEqual(pos) || /\w/.test(prevChar)) {
        return;
      }
      editor.transact(() => {
        const replaceText = `${match[1].trim()} match {}`;
        replace(replaceText);
        editor.setCursorBufferPosition(range.start.translate([0, replaceText.length - 1]));
        editor.insertNewline();
        editor.insertText("case");
      });
    });
  }));
});

こんな感じ.

最近は OCaml 書いていないので直接後置で書ける確率が高くなってる.

Q. まだ Atom をつかっているのですか?

はい.