Generalising our semantics [dt-005Z]
We return to the \mathcal {C} language we saw previously:
DefinitionThe \mathcal {C} Language [dt-005R]
Using notation similar to Backus-Naur Form (BNF): \begin {array}{lcl} \mathcal {E} & \Coloneqq & n \mid x \mid \mathcal {E}_1 + \mathcal {E}_2 \mid \mathcal {E}_1 - \mathcal {E}_2\\ \mathcal {B} & \Coloneqq & \mathsf {false} \mid \mathsf {true} \mid \neg \mathcal {B} \mid \mathcal {E}_1 = \mathcal {E}_2\\ \mathcal {C} & \Coloneqq & \mathsf {skip} \mid x := \mathcal {E} \mid \mathcal {C}_1 ; \mathcal {C}_2 \mid \mathsf {if}\ \mathcal {B}\ \mathsf {then}\ \mathcal {C}_1\ \mathsf {else}\ \mathcal {C}_2 \mid \mathsf {while}\ \mathcal {B}\ \mathsf {do}\ \mathcal {C}\ \mathsf {od}\\ x & \in & \mathcal {V}\; \textit {(variables)}\\ n & \in & \mathbb {N} \end {array}
If we wish to add other kinds of effects (beyond mutation of state) to our language, this will necessarily affect the semantic domain. Rather than rewrite the entire semantics for each possible setting, we will generalise the semantics to work over any instance of an algebraic structure, called a monad.
DefinitionMonad [dm-000U]
A monad on the category \textbf {C} consists of an endofunctor \mathcal {M} : \textbf {C} \rightarrow \textbf {C} and two operations (natural transformations) for all \mathbf {C}-objects X: \mu _X : \mathcal {M}(\mathcal {M}(X)) \rightarrow \mathcal {M}(X) and \eta _X : X \rightarrow \mathcal {M}(X), such that the following diagrams commute:
- \mu _X \circ \mathcal {M}(\mu _X) = \mu _X \circ \mu _{\mathcal {M}(X)}
- \mu _X \circ \mathcal {M}(\eta _X) = \mu _X \circ \eta _{\mathcal {M}(X)} = \mathsf {id}_{\mathcal {M}(X)}
ExampleFlat domains as monads [dt-005P]
The flat domain lifting operation (\cdot )_\bot forms a monad, where the endofunctor \mathcal {M}(X) = X_\bot , the operation \mu _X : (X_\bot )_\bot \rightarrow X_\bot collapses the two \bot values, and \eta _X : X \rightarrow X_\bot is simply \eta _X(x) = x.
Sometimes it is more convenient to work with the Kleisli composition operator, rather than the operations \mu and \eta directly.
DefinitionKleisli category [dm-000T]
For a monad (\mathcal {M}, \mu , \eta ) over the category \mathbf {C}, the Kleisli category has the same objects as \mathbf {C}, but the morphisms A \xrightarrow {h} B are those morphisms h of \mathbf {C} that have the form A \xrightarrow {h} \mathcal {M}(B). Composition is defined as follows, for morphisms A \xrightarrow {f} \mathcal {M}(B) and B \xrightarrow {g} \mathcal {M}(C): g \circ _K f = \mu _C \circ \mathcal {M}(g) \circ f Identity morphisms for this category are just \eta .
DefinitionMonadic semantics for \mathcal {C} [dt-005Q]
We define our semantics generically in terms of a monad (\mathcal {M},\mu ,\eta ), using the Kleisli composition operator for \mathcal {M}: \begin {array}{l} \mathsf {ite} : (\Sigma \rightarrow \mathbb {B}) \times (\Sigma \rightarrow \mathcal {M}(\Sigma )) \times (\Sigma \rightarrow \mathcal {M}(\Sigma )) \rightarrow (\Sigma \rightarrow \mathcal {M}(\Sigma ))\\ \mathsf {ite}(b,t,e)(\sigma ) = \begin {cases}t(\sigma ) & \text {if}\ b(\sigma ) = T \\ e(\sigma ) & \text {otherwise} \end {cases} \end {array} \begin {array}{lcl} \llbracket \cdot \rrbracket _\mathcal {C} : \mathcal {C} \rightarrow \Sigma \rightarrow \mathcal {M}(\Sigma )\\[0.4em] \llbracket \mathsf {skip} \rrbracket _\mathcal {C} & = & \eta _\Sigma \\ \llbracket x := e \rrbracket _\mathcal {C} & = & \boldsymbol {\lambda } \sigma .\ \eta _\Sigma (\sigma \left (x \mapsto \llbracket e \rrbracket _\mathcal {E}\right )) \\ \llbracket c_1 ; c_2 \rrbracket _\mathcal {C} & = & \llbracket c_2 \rrbracket _\mathcal {C} \circ _K \llbracket c_1 \rrbracket _\mathcal {C} \\[0.3em] \llbracket \textsf {if}\ b\ \textsf {then}\ c_1\ \textsf {else}\ c_2 \rrbracket _\mathcal {C} & = & \mathsf {ite}(\llbracket b \rrbracket _\mathcal {B},\llbracket c_1 \rrbracket _\mathcal {C},\llbracket c_2 \rrbracket _\mathcal {C})\\ \llbracket \textsf {while}\ b\ \textsf {do}\ c\ \textsf {od} \rrbracket _\mathcal {C} & = & \mathbf {\mathbf {fix}}(\boldsymbol {\lambda } f.\ \mathsf {ite}(\llbracket b \rrbracket _\mathcal {B}, f \circ _K \llbracket c \rrbracket _\mathcal {C}, \eta _\Sigma ) ) \end {array}
ExampleInstantiating \mathcal {C} with the lifting monad [dt-005S]
Instantiating our monadic semantics where the monad \mathcal {M} is flat domain lifting, we recover our original semantics of \mathcal {C}.
ExampleInstantiating \mathcal {C} with exceptions [dt-005T]
Let us instantiate our monadic semantics with the monad \mathcal {M}(X) = X_\bot \oplus \mathbf {1}, where the added value (from \mathbf {1}) is called \mathsf {fail}. The operation \mu _X : (X_\bot \oplus \mathbf {1})_\bot \oplus \mathbf {1} \rightarrow X_\bot \oplus \mathbf {1} collapses the two possible \bot values to \bot and the two possible \mathsf {fail} values to \mathsf {fail}, and the operation \eta _X : X \rightarrow X_\bot \oplus \mathbf {1} is the straightforward injection.
Then we may extend our language with lightweight exception constructs: \mathcal {C} ::= \dots \mid \mathsf {throw} \mid \mathsf {try}\ \mathcal {C}_1\ \mathsf {catch}\ \mathcal {C}_2 and define their semantics, leaving our existing semantics unchanged: \begin {array}{lcl} \llbracket \mathsf {throw} \rrbracket \ \sigma &=& \mathsf {fail} \\ \llbracket \mathsf {try}\ c_1\ \mathsf {catch}\ c_2 \rrbracket \ \sigma &=& \begin {cases} \llbracket c_2 \rrbracket \ \sigma & \text {if}\ \llbracket c_1 \rrbracket \ \sigma = \mathsf {fail} \\ \llbracket c_1 \rrbracket \ \sigma & \text {otherwise}\end {cases} \end {array}