annotation example definition

A total function is defined on all arguments; a pure function uses only its arguments (and captured values). A side effect is anything breaking totality or purity — the “shotgun approach” of imperative languages that makes composition of effects a case-by-case affair. Purely functional languages instead encode effects in return types: a computation with effect becomes a pure for a type constructor ; composing such computations is what a Monad provides (Kleisli Category).

Sources: DaoFP §14.1 (“Programming with Side Effects”: “Partiality”, “Logging”, “Environment”, “State”, “Nondeterminism”, “Input/Output”, “Continuation”), §14.4 (“Monad Instances”).

effectpure encodingfunctormonad note
partiality (exceptions)a -> Maybe b, a -> Either e bMaybe, Either eMaybe Monad; the caller must handle Nothing (unlike null pointers)
logging / auditinga -> Writer w bnewtype Writer w a = Writer (a, w)Writer Monad; “make the function provide all the data, and let the caller deal with the effects”
read-only environment(a, e) -> b curried to a -> Reader e bnewtype Reader e a = Reader (e -> a)Reader Monad; a delayed effect — a script run by runReader
mutable state(a, s) -> (b, s) curried to a -> State s bnewtype State s a = State (s -> (a, s))State Monad; runState is function application
nondeterminisma -> [b] (many-worlds: all results at once)[]List Monad
input/outputa -> IO bopaque IO (no runIO)IO Monad; the program generates a script executed by the runtime; main :: IO ()
continuationa -> Cont r bnewtype Cont r a = Cont ((a -> r) -> r)Continuation Monad; covariant since a is in doubly negative position;

At this stage nothing is assumed of — not even functoriality; that comes with effect composition. Imperative languages hard-code some of these (exceptions ≈ Either, tasks ≈ continuations, coroutines ≈ Do Notation).

Docs: plain Julia — Catlab has no dedicated API for this; related: Catlab v0.16 docs · GATlab standard library

# the same encodings in Julia
partial(x) = x == 0 ? nothing : Some(1 / x)              # Maybe
logging(x) = (x^2, "squared $x")                          # Writer: value paired with a log
reader(x) = env -> x + env[:offset]                       # Reader: delayed access to the environment
state(x) = s -> (x + s, s + 1)                            # State: returns value and new state
nondet(x) = [x, -x]                                       # List: all results at once
newtype Writer w a = Writer (a, w)
newtype Reader e a = Reader (e -> a)
runReader :: Reader e a -> e -> a
runReader (Reader h) e = h e
newtype State s a = State (s -> (a, s))
runState :: State s a -> s -> (a, s)
runState (State h) s = h s
newtype Cont r a = Cont ((a -> r) -> r)
runCont :: Cont r a -> (a -> r) -> r
runCont (Cont f) k = f k
main :: IO ()
main = getLine >>= putStrLn          -- the IO script: echo a line