私は最近、scala用の新しい「仮想化」パターンマッチャーについてかなりの数の 言及を見てきました。それが実際に何であったかを説明するメモを逃しました...
2 に答える
「仮想化された」パターンマッチャーは、既存のマッチャーを書き直したものです。これを行う動機は、2.10には関係のない、ポリモーフィックな組み込みDSLのパターンマッチングの仮想化をサポートすることでした。
Iulianが以下のコメントで述べているように、これはfor-comprehensionsのコンパイル方法と非常に似ています。コードを直接生成する代わりに、などに変換されforeach
ますmap
。filter
パターンマッチングは、一連のメソッド呼び出しに変換され、DSLが上書きする可能性があります。 。デフォルトの実装は現在のセマンティクスを尊重します。課題は、現在のセマンティクスと同じくらい効率的にすることです。Adriaanはこの目標に非常に近いようです。「仮想化」実装はより単純であり、現在の実装のいくつかのバグを修正します。
「ポリモーフィック組み込みDSL」は、JVMで実行されることを想定していないプログラムをscalaで作成する可能性があるという考えです。つまりscalac
、プログラムが実行していることを説明する出力を生成します。その後、これを特定のアーキテクチャに対して再コンパイルできます。そのようなことはScalaDays2011で話題になっています。
この書き直しは、最終的には標準のscalaパターンマッチャーになります。古いパターンマッチャーは(私が理解しているように)保守不可能でした。
悲しいことに、(唯一の)既存の答えはジューシーなビットが少なく、解説のリンクが壊れています。それで、ここにジュースを追加してみましょう。他の理由がなければ、将来実際に何かをすることにしたときの私自身の参照です。この答えは、私が行うすべてのグーグル検索の上にあるからです。
前述のように、仮想化されたパターンマッチャーは、Scalaコンパイラーがパターンマッチングを処理する方法を書き直したものです。それは多くの目的を果たし、その「仮想化」の部分は、仮想化されたscalaの取り組みの一部であることを意味します。この作業は、マクロとは少し逆です。コンパイル時に「実行」され、実行時に移動するものが必要です。
たとえば、スコープ内に適切な定義が存在する場合、次のようなステートメントがあります。
if (false) 1 else 2
バイトコードブランチとリテラルにコンパイルされる代わりに、またはリテラル「2」に最適化される代わりに、実際には次のステートメントとしてコンパイルされます。
__ifThenElse(false, 1, 2)
詳細とこれが役立つ例については、 scala仮想化ウィキを参照してください。
ただし、パターンマッチャーの書き換えには多くの目的があると言いました。もう1つの非常に重要な目標は、古いパターンマッチャーであったスパゲッティコード、フルケースまたはスペシャルケース、コーナーケース、バグを、より簡単に推論、拡張、改善できるものに変えることでした。この書き直しにより多くの問題が修正されたため、パターンマッチャーに関連する問題のサンプルコードを実行し、問題が機能するにつれて問題を「修正済み」としてマークする問題リストを確認しました。独自の新しいバグがありますが、規模ははるかに小さくなります。
現在、新しいパターンマッチャーがどのように機能するかについての情報はほとんどありませんが、基本的には、Option
モナドを使用してコンパイラーに「実装」されるいくつかのメソッド呼び出しに変換されます。次に、最適なバイトコードを生成する最適化フェーズに入ります。
-Xexperimental
フラグの後ろにロックされていますが、独自のマッチャーを導入することは可能です。Scalaのテストスイートからコピーした次のコードを、そのフラグがある場合とない場合で試してください。
trait Intf {
type Rep[+T]
type M[+T] = Rep[Maybe[T]]
val __match: Matcher
abstract class Matcher {
// runs the matcher on the given input
def runOrElse[T, U](in: Rep[T])(matcher: Rep[T] => M[U]): Rep[U]
def zero: M[Nothing]
def one[T](x: Rep[T]): M[T]
def guard[T](cond: Rep[Boolean], then: => Rep[T]): M[T]
def isSuccess[T, U](x: Rep[T])(f: Rep[T] => M[U]): Rep[Boolean] // used for isDefinedAt
}
abstract class Maybe[+A] {
def flatMap[B](f: Rep[A] => M[B]): M[B]
def orElse[B >: A](alternative: => M[B]): M[B]
}
implicit def proxyMaybe[A](m: M[A]): Maybe[A]
implicit def repInt(x: Int): Rep[Int]
implicit def repBoolean(x: Boolean): Rep[Boolean]
implicit def repString(x: String): Rep[String]
def test = 7 match { case 5 => "foo" case _ => "bar" }
}
trait Impl extends Intf {
type Rep[+T] = String
object __match extends Matcher {
def runOrElse[T, U](in: Rep[T])(matcher: Rep[T] => M[U]): Rep[U] = ("runOrElse("+ in +", ?" + matcher("?") + ")")
def zero: M[Nothing] = "zero"
def one[T](x: Rep[T]): M[T] = "one("+x.toString+")"
def guard[T](cond: Rep[Boolean], then: => Rep[T]): M[T] = "guard("+cond+","+then+")"
def isSuccess[T, U](x: Rep[T])(f: Rep[T] => M[U]): Rep[Boolean] = ("isSuccess("+x+", ?" + f("?") + ")")
}
implicit def proxyMaybe[A](m: M[A]): Maybe[A] = new Maybe[A] {
def flatMap[B](f: Rep[A] => M[B]): M[B] = m + ".flatMap(? =>"+ f("?") +")"
def orElse[B >: A](alternative: => M[B]): M[B] = m + ".orElse("+ alternative +")"
}
def repInt(x: Int): Rep[Int] = x.toString
def repBoolean(x: Boolean): Rep[Boolean] = x.toString
def repString(x: String): Rep[String] = x
}
object Test extends Impl with Intf with App {
println(test)
}
フラグなしの結果は、まさにあなたが期待するものです。
scala> Test.main(null)
bar
ただし、を使用-Xexperimental
すると、一致する代替の「エンジン」がコンパイルされます。
scala> Test.main(null)
runOrElse(7, ?guard(false,?).flatMap(? =>one(foo)).orElse(one(bar)))
詳細については、PatternMatchingおよびMatchMonadInterfaceのscaladocsも参照してください。
免責事項:上記は、2.10.0以降のマスターブランチのScalaバージョンから抽出して実行したものであるため、違いがある可能性があります。しかし、私はそれをテストするための純粋な2.10.0または2.10.1環境に悲しいことに欠けていることに気づきました。