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

デフォルトハンドラ

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

Flix は デフォルトハンドラ(default handler) をサポートしています。これは、 エフェクトが、そのエフェクトを IO エフェクトへと変換するハンドラを宣言できる ことを意味します。これにより、main(および @Test が付与された任意のメソッド)は、 run-with ブロックでハンドラを明示的に提供することなく、そのエフェクトを 使えるようになります。

たとえば、次のように書けます。

use Sys.Env
use Time.Clock

def main(): Unit \ { Clock, Env, Logger } =
    let ts = Clock.currentTime(TimeUnit.Milliseconds);
    let os = Env.getOsName();
    Logger.info("UNIX Timestamp:   ${ts}");
    Logger.info("Operating System: ${os}")

これを Flix コンパイラは次のように変換します。

use Sys.Env
use Time.Clock

def main(): Unit \ IO =
    run {
        let ts = Clock.currentTime(TimeUnit.Milliseconds);
        let os = Env.getOsName();
        Logger.info("UNIX Timestamp:   ${ts}");
        Logger.info("Operating System: ${os}")
    } with Clock.runWithIO
      with Env.runWithIO
      with Logger.runWithIO

すなわち、Flix コンパイラは Clock.runWithIO、Env.runWithIO、 Logger.runWithIO の呼び出しを自動的に挿入します。これらは、それぞれの エフェクトに対するデフォルトハンドラです。

たとえば、Clock.runWithIO は次のように宣言されています。

use Time.Clock

@DefaultHandler
pub def runWithIO(f: Unit -> a \ ef): a \ (ef - Clock) + IO = ...

デフォルトハンドラは @DefaultHandler アノテーションを使って宣言します。 各エフェクトが持てるデフォルトハンドラは高々 1 つであり、それはそのエフェクトの コンパニオンモジュール(companion module)の中になければなりません。

デフォルトハンドラは、次の形式のシグネチャを持たなければなりません。

def runWithIO(f: Unit -> a \ ef): a \ (ef - E) + IO

ここで E はエフェクトの名前です。

デフォルトハンドラを持つエフェクトは、テストの中で使えます。たとえば:

@Test
def myTest01(): Unit \ { Assert, Logger } = 
    Logger.info("Running test!");
    Assert.assertEq(expected = 42, 42)

多相エフェクト

多相エフェクト もデフォルトハンドラを持つことができます。たとえば:

mod Emit {
    pub eff Emit[t] {
        def emit(x: t): Unit
    }

    @DefaultHandler
    pub def runWithIO(f: Unit -> a \ ef): a \ (ef - Emit[t]) + IO =
        run {
            f()
        } with handler Emit {
            def emit(_, resume) = resume()
        }
}

def main(): Unit \ { Emit[Int32], IO } =
    Emit.emit(42);
    println("Done")

ここで Emit のデフォルトハンドラは、emit された値を単に捨てます。

多相エフェクト E[t] のデフォルトハンドラは、次の形式のシグネチャを持たなければなりません。

def runWithIO(f: Unit -> a \ ef): a \ (ef - E[t]) + IO

ここで t は型変数です。このシグネチャにはトレイト制約を持たせることができません。例えば with ToString[t] を追加することはできません。これは、デフォルトハンドラがすべての型 t に対して機能しなければならないためです。これが、上記のデフォルトハンドラが emit された値を表示できない理由です。