これは私の前の質問のフォローアップです
次のような XML を検証する必要があるとします。
<a><a1>xxx<a1/><a2>yyy</a2><a3>zzz</a3></a>
ルート要素にラベルがあり、この順序でa
子<a1>xxx</a1>
、があることを確認する必要があります。<a2>yyy</a2>
<a3>zzz</a3>
List[String]
エラーを収集し、次のような単一の XML 要素を検証する関数を定義するために使用したいと思います。
type ValidateSingleElement = Elem => List[String]
これで、特定の XML 要素のラベル、テキスト、および属性を検証する関数を作成できるようになりました。
val label : String => ValidateSingleElement = ...
val text : String => ValidateSingleElement = ...
val attr : (String, String) => ValidateSingleElement = ...
はモノイドである|+|
ため、これらの関数を構成できます。ValidateSingleElement
val a1 = label("a1") |+| text("xxx") // validate both label and text
ここで、特定の要素の子を検証する関数が必要です。このような関数を作成するには、要素 のシーケンスを検証する別の関数が必要です
val children: ValidateElements => ValidateSingleElement = ...
はValidateElements
次のように定義されます。
type ValidateElements = List[Elem] => Writer[List[String], List[Elem]]
一連の要素をトラバースしながらエラーを収集するためにList[String]
andモナドを使用しています。Writer
これで、特定の要素の子を検証する関数を作成できます。
val children: ValidateElements => ValidateSingleElement =
validateElements => {e =>
val kids = e.child collect {case e:Elem => e}
val writer = validateElements(kids.toList)
writer.written
}
...そして、シーケンスの最初の要素を検証します:
val child: ValidateSingleElement => ValidateElements = validate => {
_ match {
case e:es => Writer(validate(e), es)
case _ => Writer(List("Unexpected end of input"), Nil)
}
}
ValidateElements
最後に、次のように再定義できますKleisli
type ErrorsWriter[A] = Writer[List[String], A]
type ValidateElements = Kliesli[ErrorsWriter, List[Elem], List[Elem]]
...そして、関数の代わりにchild
を返すように書き直します。Kleisli
child
とchildren
私が書くことができる両方を考えると、a
上記の XML の検証関数:
val a1 = label("a1") |+| text("xxx")
val a2 = label("a2") |+| text("yyy")
val a3 = label("a3") |+| text("zzz")
val a = label("a") |+| children(child(a1) >=> child(a2) >=> child(a3))
それは理にかなっていますか?このデザインをどのように修正/拡張しますか?