diff --git a/book.toml b/book.toml index 5ac2de23..4c7e0067 100644 --- a/book.toml +++ b/book.toml @@ -1,7 +1,6 @@ [book] authors = ["Magnus Madsen"] language = "en" -multilingual = false src = "src" title = "Programming Flix" diff --git a/src/SUMMARY.md b/src/SUMMARY.md index fe09b1e2..4bb6682b 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -32,6 +32,7 @@ - [Effect Polymorphism](./effect-polymorphism.md) - [Effects and Handlers](./effects-and-handlers.md) - [Library Effects](./library-effects.md) + - [Default Handlers](./default-handlers.md) - [Effect-Oriented Programming](./effect-oriented-programming.md) - [Modules](./modules.md) - [Declaring Modules](./declaring-modules.md) diff --git a/src/default-handlers.md b/src/default-handlers.md new file mode 100644 index 00000000..419dafa0 --- /dev/null +++ b/src/default-handlers.md @@ -0,0 +1,64 @@ +## Default Handlers + +> **Note:** This feature requires Flix version 0.67.0 + +Flix supports **default handlers** which means that an effect can declare a +handler that translates the effect into the `IO` effect. This allows `main` (and +any method annotated with `@Test`) to use that effect without explicitly +providing a handler in a `run-with` block. + +For example, we can write: + +```flix +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}") + +``` + +which the Flix compiler translates to: + +```flix +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 +``` + +That is, the Flix compiler automatically inserts calls to `Clock.runWithIO`, +`Env.runWithIO`, and `Logger.runWithIO` which are the default handlers for their +respective effects. + +For example, `Clock.runWithIO` is declared as: + +```flix +@DefaultHandler +pub def runWithIO(f: Unit -> a \ ef): a \ (ef - Clock) + IO = ... +``` + +A default handler is declared using the `@DefaultHandler` annotation. Each +effect may have at most one default handler, and it must reside in the companion +module of that effect. + +A default handler must have a signature of the form: + +```flix +def runWithIO(f: Unit -> a \ ef): a \ (ef - E) + IO +``` +where `E` is the name of the effect. + +We can use effects with default handlers in tests. For example: + +```flix +@Test +def myTest01(): Unit \ {Assert, Logger} = + Logger.info("Running test!"); + Assert.assertEq(expected = 42, 42) +```