Effects

An Effect is a struct that represents a request for a side effect. It's a message from your computation to the outside world.

Defining effects with #[effect]

To define an effect, annotate a struct with #[effect(ResumeType)]. The resume type defines what value the computation receives back after the effect is handled.

use corophage::prelude::*;

#[effect(())]
pub struct Log(pub String);

#[effect(String)]
pub struct FileRead(pub String);

#[effect(Never)]
pub struct Cancel;

The macro supports lifetimes, generics, named fields, and borrowed resume types:

// Lifetime parameters
#[effect(bool)]
pub struct Borrow<'a>(pub &'a str);

// Generic parameters
#[effect(T)]
pub struct Generic<T: std::fmt::Debug + Send + Sync>(pub T);

// Named fields
#[effect(Vec<u8>)]
pub struct ReadDir { pub path: String, pub recursive: bool }

// The resume type may reference the GAT lifetime 'r
#[effect(&'r str)]
pub struct Lookup(pub String);

The declare_effect! macro

Alternatively, you can use the declare_effect! macro for a more concise syntax:

use corophage::prelude::*;

declare_effect!(Log(String) -> ());
declare_effect!(FileRead(String) -> String);
declare_effect!(Cancel -> Never);

The macro supports lifetimes, generics, named fields, and borrowed resume types:

// Lifetime parameters
declare_effect!(Borrow<'a>(&'a str) -> bool);

// Generic parameters
declare_effect!(Generic<T: std::fmt::Debug>(T) -> T);

// Named fields
declare_effect!(FileRead { path: String, recursive: bool } -> Vec<u8>);

// The resume type may reference the GAT lifetime 'r
declare_effect!(Lookup(String) -> &'r str);

Effect sets with Effects!

Effects are grouped into sets using the Effects! macro:

type MyEffects = Effects![Log, FileRead, Cancel];

This creates a type-level list (coproduct) of effects. The type system tracks which effects have been handled and prevents you from running a program until all effects have handlers.