自然言語テキストを処理する関数をいくつか定義したいと思います。これらの関数はそれぞれ、テキストにいくつかの「注釈」を追加します。
class Annotation(val begin: Int, val end: Int)
class Sentence(begin: Int, end: Int) extends Annotation(begin, end)
class Token(begin: Int, end: Int) extends Annotation(begin, end)
したがって、Token アノテーションを追加する Tokenizer 関数、Sentence アノテーションを追加する SentenceSegmenter 関数などを使用する場合があります。これらの関数には、実行できる順序にいくつかの制約があります。たとえば、Tokenizer には Sentence アノテーションが必要な場合があるため、SentenceSegmenter の後に実行する必要があります。この場合、誤ってこれらの関数を間違った順序で作成した場合にコンパイル エラーが発生するようにしたいと考えています。したがってsentenceSegmenter andThen tokenizer
、コンパイルする必要がありますが、コンパイルしtokenizer andThen sentenceSegmenter
ないでください。
以下は私の試みです。テキスト用に特別なコンテナー タイプを定義しました。このタイプ パラメーターは、テキストに追加された注釈を (複合型を介して) 指定し、関数は型パラメーターを適切に指定して、前提条件が満たされるまで実行できないようにします。複合タイプの一部です。
trait AnalyzedText[T] {
def text: String
def ++[U](annotations: Iterator[U]): AnalyzedText[T with U]
}
val begin: (AnalyzedText[Any] => AnalyzedText[Any]) = identity
def sentenceSegmenter[T]: (AnalyzedText[T] => AnalyzedText[T with Sentence]) = ???
def tokenizer[T <: Sentence]: (AnalyzedText[T] => AnalyzedText[T with Token]) = ???
// compiles
val pipeline = begin andThen sentenceSegmenter andThen tokenizer
// fails to compile -- good!
//val brokenPipeline = begin andThen tokenizer andThen sentenceSegmenter
ここまでは順調ですね。関数の 1 つを実際に定義しようとすると、問題が発生します。たとえば、次のように定義したいと思いますtokenizer
。
def tokenizer[T <: Sentence]: (AnalyzedText[T] => AnalyzedText[T with Token]) =
text => text ++ "\\S+".r.findAllMatchIn(text.text).map(m => new Token(m.start, m.end))
しかし、Scala コンパイラーはメソッドの型引数を推測する方法を理解できず、++
手動で型パラメーターを指定しない限りtext.++[Token](...)
、次のエラーが発生します。
type mismatch; found: Iterator[Token] required: Iterator[Nothing]
この型パラメーターを推論する方法はありますか? あるいは、私は問題について間違って考えていますか? Scala でこれらの種類の関数構成の制約をキャプチャするより良い方法はありますか?