Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion book.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
[book]
authors = ["Magnus Madsen"]
language = "en"
multilingual = false
src = "src"
title = "Programming Flix"

Expand Down
1 change: 1 addition & 0 deletions src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
64 changes: 64 additions & 0 deletions src/default-handlers.md
Original file line number Diff line number Diff line change
@@ -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)
```