Applicative For-Yield
💡 お知らせ: このドキュメントはAIによって翻訳されています。表現に違和感がある場合は、原文(英語)を参照するか、翻訳にご協力ください。
Flix は、モナド的(monadic)な forM 式に加えて、Applicative トレイト(Trait)を基盤とするアプリカティブ(applicative)な forA 式をサポートしています。forA 構文を使うと、Validation[e, t] データ型を用いたエラー処理コードを簡単に書けます。
Validation を扱う
forA 式を使うと、すべてのエラーを収集しながらユーザー入力を検証(validate)できます。
enum Connection(String, String)
enum InvalidInput {
case InvalidUserName,
case InvalidPassword
}
def validateUser(s: String): Validation[InvalidInput, String] =
if (8 <= String.length(s) and String.forAll(Char.isLetter, s))
Validation.Success(s)
else
Validation.Failure(Nec.singleton(InvalidInput.InvalidUserName))
def validatePass(s: String): Validation[InvalidInput, String] =
if (12 <= String.length(s) and String.length(s) <= 20)
Validation.Success(s)
else
Validation.Failure(Nec.singleton(InvalidInput.InvalidPassword))
def connect(u: String, p: String): Validation[InvalidInput, Connection] =
forA (
user <- validateUser(u);
pass <- validatePass(p)
) yield Connection.Connection(user, pass)
次の式は、
connect("Lucky Luke", "Ratata")
次のように評価されます。
Failure(Nec#{InvalidUserName, InvalidPassword})
これには入力検証のエラーが 両方とも 含まれています。一方、次の式は、
connect("luckyluke", "password12356789")
次のように評価されます。
Success(Connection(luckyluke, password12356789))
アプリカティブは独立した計算である
モナド的な forM 式では、あるモナド操作の結果を別のモナド操作の入力として使うことができます。たとえば次のようになります。
forM(x <- Some(123); y <- Some(x))
yield (x, y)
ここでは y の値が x に依存しています。つまり、x と y の計算は独立していません。
これと同じことをアプリカティブな forA 式で試すと、
forA(x <- Some(123); y <- Some(x))
yield (x, y)
Flix コンパイラは次のコンパイルエラーを出力します。
❌ -- Resolution Error --------------
>> Undefined name 'x'.
10 | y <- Some(x)
^
name not found
これは、x と y の計算が 独立している ため、y の値を定義する時点で x の値がスコープ内に ない からです。
脱糖(Desugaring)
forA 式は、Functor.map と Applicative.ap を使うための糖衣構文です。
たとえば、次の式は、
let o1 = Some(21);
let o2 = Some(42);
forA(x <- o1; y <- o2)
yield x + y;
次のように脱糖されます。
Applicative.ap(Functor.map(x -> y -> x + y, o1), o2)