kmizuの日記

プログラミングや形式言語に関係のあることを書いたり書かなかったり。

斜め読み論文紹介(1)「From APIs to Languages: Generalising Method Names」

タイトルは、斜め読み論文紹介ですが、

togetter.com

↑の辺の企画の亜種だと思ってください。気が向いたらときどき書いていこうと思います。第一回は、From APIs to Languages: Generalising Method Namesです*1

唐突ですが、Smalltalkという言語があります。オブジェクト指向を発明したとかいう言語ですが(テキトーな言い方だ…)ここでは全く関係がないのでおいておきます。Smalltalkの系列の言語は、メソッド名の構成に関してやや特殊なところがあります。それは、メソッド名をいくつかの断片に分けて記述するところです。

たとえば、辞書に値を突っ込むには、次のようにします:

kmizu := Dictionary new.
kmizu at:'Age' put:'32'.
kmizu at:'Sex' put:'Male'.
kmizu at:'Name' put:'Kota Mizushima'

ここで、at:put:で合わせて一つのメソッド名を構成しているのが、Smalltalk系列(Objective-Cも含む)のメソッドの特異なところです。これは、いわゆる名前付き引数とも違っていて、通常、メソッドの断片の順序は変更できません(もし最近のSmalltallkだとできるとかあれば教えてください)。

ここまでが準備です。さて、このように、メソッドをメソッド名の断片に分けられるのなら、その断片について

  • 二つの断片をくっつけたり
  • 二つの断片の片方だけOKだったり(|)
  • 繰り返したり(0回以上または1回以上)(*,+)
  • オプショナルだったり(?)

といった規則を使って、断片同士くっつけられるはず!、というのがこの論文のポイントです。メソッド名の断片を基本単位とした、正規表現のようなものが書けるようになった、という感じでしょうか。このような機構を指して、著者らは「Generalized Method Names」と呼んでいます。以下は、論文中に出てくるサンプルコードです。

method use(x) +addRatio(y1, y2) ?multiplyBy(z) {
  var value := x
  for (y1) and (y2) do { a, b −>
    value := value + a / b
  }
  for (z) do { v −> 
    return value * v 
  }
  return value
}
use(0) addRatio(1, 2) addRatio(2, 4)
       multiplyBy(6) // −> 6
use(1) addRatio(2, 3)
       multiplyBy(2) // −> 3 1/3

useで初期値を設定して、addRatio有理数を足しこんで、multiplyByで掛ける、という具合です。ポイントは、addRatioにはprefixとして+が付いており、useに続いて、addRatioを1回以上繰り返して呼び出せること、それに続いて、multiplyByを0回または1回呼び出せることです。

このGeneralized Method Namesを使うことで、

  • if-elseifのような制御構造
  • パターンマッチ
  • SQLライクなクエリ言語
  • try-catch-finallyのような例外処理機構
  • テスト記述言語

といったものをすべて統一的な機構で扱えるのです(というのが著者らの主張です)。

さらに、これらの、メソッドの断片について、部分型(Subtype)関係が定義されているのが面白いところです。たとえば、

a() *(b() | c()) <: a() *b() *c()

というサブタイプ関係が成り立つそうです。結構ややこしいので詳細は論文を読んでもらうことにします(というか、自分もサブタイプ関係のところはちゃんと読んでいない)。

ともあれ、メソッド名の断片を基本単位としてなんかするというのは、結構昔から聞いたことがあるテーマではありますが、結構綺麗に一般化できるのだなーというのが素直な感想です。

あと、ちょっと面白いのは、メソッド断片に対するマッチングはgreedyに行われるので、たとえば、

method a(x) *b(y) b(z) { ... }

のような、決してマッチすることのない(二つ目のb(y)で全部`bの出現を食べてしまうため)メソッド定義もできちゃうところですね。

テストDSLの例みると、

method scenario(desc)
  ?(Given(context) *And(contexts))
  When(event)
  Then(expr) ShouldBe(val)
  *(And(exprs) ShouldBe(vals)) {...}

method feature(name)
  ?background(initialisation)
  +(scenario(desc)
  ?(Given(context) *And(contexts))
  When(event)
  Then(expr) ShouldBe(val)
  *(And(exprs) ShouldBe(vals)))

のような、超メソッド解決がややこしそうな例がありますが、これはまあ柔軟性の代償というところでしょうか。というわけで、なんとなく数日前に読んだ論文を紹介してみました。他の方々もなんかおもしろんぶんがあれば紹介してもらえると嬉しいかもです。

*1:Proceedings of the 11th Symposium on Dynamic Languages 2015。自分はそれほど詳しいわけではないですが、動的型付き言語関係の国際会議のようです