Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Monadic For-Yield

💡 お知らせ: このドキュメントはAIによって翻訳されています。表現に違和感がある場合は、原文(英語)を参照するか、翻訳にご協力ください。

Flix は、Scala の for 内包表記や Haskell の do 記法に似た、モナドの forM-yield 構文をサポートしています。forM 構文は、pointflatMap(これらは Monad トレイトによって提供されます)を使用するための糖衣構文です。forM 構文は、empty(これは MonadZero トレイトによって提供されます)を使用する guard 式もサポートしています。

たとえば、次のモナドの forM 式:

let l1 = 1 :: 2 :: Nil;
let l2 = 1 :: 2 :: Nil;
forM (x <- l1; y <- l2)
    yield (x, y)

は、次のリストに評価されます:

(1, 1) :: (1, 2) :: (2, 1) :: (2, 2) :: Nil

ガード式を使用する

forM 式の中で ガード式 を使用することができます。たとえば、次のプログラム:

let l1 = 1 :: 2 :: Nil;
let l2 = 1 :: 2 :: Nil;
forM (x <- l1; y <- l2; if x < y)
    yield (x, y)

は、次のリストに評価されます:

(1, 2) :: Nil

Option と Result を扱う

forM を使って Option データ型を扱うこともできます。たとえば:

def divide(x: Int32, y: Int32): Option[Int32] = 
    if (y == 0) None else Some(x / y)

def f(): Option[Int32] = 
    forM (
        x <- divide(5, 2);
        y <- divide(x, 8);
        z <- divide(9, y)
    ) yield x + y + z

ここで、関数 fNone を返します。なぜなら x = 5 / 2 = 2 であり、2 / 8 = 0 となるため、最後の除算が失敗するからです。

同様に、forM を使って Result[e, t] データ型を扱うことができます。たとえば:

use Sys.Console

def main(): Result[String, Unit] \ IO =
    println("Please enter your first name, last name, and age:");
    forM (
        fstName <- Console.readln();
        lstName <- Console.readln();
        ageLine <- Console.readln();
        ageNum  <- Int32.parse(10, ageLine)
    ) yield {
        println("Hello ${lstName}, ${fstName}.");
        println("You are ${ageNum} years old!")
    }

ここで main は、ユーザーに名(first name)、姓(last name)、年齢の入力を促します。Console.readln の各呼び出しは、エラーまたは入力文字列のいずれかである Result[String, String] 値を返します。したがって、ローカル変数 fstNamelstNameageLineString です。Int32.parse を使用して ageLineInt32 にパースします。これは Result[String, Int32] 値を返します。すべての操作が成功した場合、あいさつを表示して Ok(())(すなわち UnitOk)を返します。そうでなければ、Err(msg) 値を返します。

その他の Monad を扱う

forM は、ChainNel(空でないリスト)を含む、他の種類の Monad でも使用できます。たとえば、次のように書くことができます:

let l1 = Nel(1, 2 :: Nil);
let l2 = Nel(1, 2 :: Nil);
forM (x <- l1; y <- l2)
    yield (x, y)

これは、次の空でないリストに評価されます:

Nel((1, 1), (1, 2) :: (2, 1) :: (2, 2) :: Nil)

注意: 空でないリストでは if-ガードを使用することができません。なぜなら、そのような if-ガードは MonadZero トレイトのインスタンスを必要としますが、空でないリストはこれを実装していないからです(そのようなリストは空になることができないため)。

脱糖

forM 式は、Monad.flatMapApplicative.pointMonadZero.empty を使用するための糖衣構文です。

たとえば、次の式:

let l1 = 1 :: 2 :: Nil;
let l2 = 1 :: 2 :: Nil;
forM (x <- l1; y <- l2; if x < y)
    yield (x, y)

は、次のように脱糖されます:

Monad.flatMap(x -> 
    Monad.flatMap(y -> 
        if (x < y)
            Applicative.point((x, y))
        else 
            MonadZero.empty(), 
    l2), 
l1);