PageRenderTime 67ms CodeModel.GetById 30ms RepoModel.GetById 0ms app.codeStats 0ms

/writeup/effects.lhs

https://github.com/christopherschwaab/freezing-octo-robot
Haskell | 308 lines | 121 code | 18 blank | 169 comment | 1 complexity | f4b2f74dc3af3ea1a41ccc2184346f7e MD5 | raw file
  1. \documentclass[preprint,cm,10pt]{sigplanconf}
  2. %include polycode.fmt
  3. %include forall.fmt
  4. %\usepackage{color}
  5. %\input{solarized.theme}
  6. %%include effects.fmt
  7. \usepackage{amsmath}
  8. \usepackage{amsmath}
  9. \title{Algebraic Reasoning over Algebraic Effects}
  10. \authorinfo{Steven Keuchel \and Tom Schrijvers}
  11. {University of Ghent}
  12. {\{steven.keuchel,tom.schrijvers\}@ugent.be}
  13. \authorinfo{Christopher Schwaab}
  14. {}
  15. {christopher.schwaab@gmail.com}
  16. \date{}
  17. \bibliography{refs}
  18. \bibliographystyle{plain}
  19. \long\def\ignore#1{}
  20. \begin{document}
  21. \ignore{
  22. \begin{code}
  23. {-# LANGUAGE DataKinds, KindSignatures, GADTs,
  24. MultiParamTypeClasses, FlexibleInstances,
  25. PolyKinds, TypeFamilies, TypeOperators,
  26. RankNTypes, FlexibleContexts, DeriveFunctor,
  27. OverlappingInstances #-}
  28. import qualified Data.Map as M
  29. import Control.Applicative
  30. import Control.Monad
  31. import Control.Monad.Coroutine
  32. import Control.Monad.Error
  33. import qualified Control.Monad.State as MS
  34. import Control.Monad.Writer hiding (lift)
  35. \end{code}
  36. }
  37. \maketitle
  38. \begin{abstract}
  39. \end{abstract}
  40. \section{Introduction}
  41. One of the great advantages of functional programming is the ease with
  42. which programs can be algebraically manipulated; however this simple
  43. purity can be at odds with actual software whose key ingredients
  44. include side-effects. Modern functional systems solve this problem by
  45. structuring programs using monads to uniformly specify and encapsulate
  46. effects---in this way a term's type specifies its possible behaviors.
  47. Moreover this approach to writing effectful programs in a pure setting
  48. has been shown to retain the simple, equational style of reasoning
  49. enjoyed by its effect-free counterpart~\cite{reasoning about effects,
  50. just do it}. Still, monads present difficulties such as how to
  51. separately introduce and combine new behaviors. In turn arguably the
  52. most popular solution to this problem---using monad
  53. \emph{transformers}---comes with yet another set of problems.
  54. \emph{Algebraic effects}~\cite{algebraic effects} offer an alternative
  55. approach to structuring programs using free monads in which the
  56. explicit order of effects is no longer exposed. Moving to a regular
  57. method of structuring effects allows for a simple description of both
  58. how to combine individual effects as well as how to invoke
  59. computations requiring only a subset of the current effect set.
  60. While algebraic effects offer an attractive simplicity, they are also
  61. less expressive than the direct monadic approach because the
  62. interaction between effects can have real semantic value. As an
  63. example, threading state through a non-deterministic computation is
  64. not equivalent to performing a non-deterministic, stateful computation
  65. admitting backtracking. Moreover the free-syntax interpretive
  66. approach to effect handling appears to complicate reasoning because
  67. the handler can potentially change the meaning of an operation.
  68. Despite the small loss in expressivity, effect handlers appear to
  69. offer an approach to organizing computational behavior in a way that
  70. is both flexible and modular that is worth exploration.
  71. Despite the great success that can be attributed to monads Gibbons and
  72. Hinze~\cite{just do it} point out that little work has been done on
  73. formal reasoning about functional programs exhibiting effects.
  74. Inspired by their elegant approach to axiomatic reasoning over monadic
  75. computations we show the same principle naturally adapts to the
  76. setting of algebraic effects.
  77. In this paper we begin with a brief background to reasoning in the
  78. presence of effects. Following we adapt the algebraic effects DSL
  79. given by Brady~\cite{brady} to a Haskell setting, detailing our
  80. implementation. We end by showing that reasoning over programs with
  81. algebraic effects is both simple and direct, without any loss in
  82. modularity. Although in the paper proofs are given by hand, to boost
  83. confidence the example given has been mechanized in Agda.
  84. \section{Background}
  85. The idea behind Gibbons' and Hinze's approach to reasoning about
  86. programs with effects is to consider operations only by their
  87. algebraic laws. This alone is enough to manipulate programs in useful
  88. ways via term substitution---as an example when trying to show the
  89. correctness of some optimization. To exemplify the idea consider the
  90. simple effect-polymorphic counter described in \emph{MRI (FIXME
  91. presumably we should refer to this? And how should we do so, by the
  92. full title?)}
  93. < tick :: StateM Int m => m ()
  94. < tick = get >>= put . (+1)
  95. Clearly calling $tick$, $n$ times is equivalent to adding $n$ to the current
  96. state, but how can we formalize this?
  97. \subsection{Reasoning about State}
  98. The behavior of a stateful computation can be captured by five easy
  99. laws~\cite{MRI} that all implementations of $\mathrm{StateM}$ satisfy.
  100. \begin{align*}
  101. get \gg m &\equiv m \tag{GetQuery} \\
  102. get \gg= \lambda s \rightarrow get \gg= f \; s &\equiv get \gg= \lambda s \rightarrow f \; s \; s \tag{GetGet} \\
  103. put \; x \gg put \; y &\equiv put \; y \tag{PutPut} \\
  104. put \; x \gg get &\equiv put \; x \gg return \; x \tag{PutGet} \\
  105. get \gg= put &\equiv return \; () \tag{GetPut}
  106. \end{align*}
  107. In order these can be taken to mean that
  108. \begin{enumerate}
  109. \item reading the state alone has no effect on the rest of the program, \\
  110. \item successive reads have no effect, reading state once is sufficient, \\
  111. \item putting a new state overwrites previously written values, \\
  112. \item reading a state just written is equivalent to returning the value that was
  113. written and, \\
  114. \item rewriting the current state has no effect.
  115. \end{enumerate}
  116. These laws are enough to show
  117. \begin{equation*}
  118. rep \; n \; tick \equiv get \gg= put \circ (+n)
  119. \end{equation*}
  120. Where $rep$ is defined as
  121. < rep 0 mx = return ()
  122. < rep (n+1) mx = mx >>= rep n mx
  123. Oliveira et. al.~\cite{mri} give a proof by induction on $n$, with the base case
  124. \begin{align*}
  125. &\quad rep \; 0 \; (get \gg= put \circ (+1)) \\
  126. &\equiv \{- \text{unfold rep 0} -\} \\
  127. &\quad return \; () \\
  128. &\equiv \{- \text{GetPut law} -\} \\
  129. &\quad get \gg= put \\
  130. &\equiv \{- \text{id is neutral element of function composition} -\} \\
  131. &\quad get \gg= put \circ id \\
  132. &\equiv \{- \text{0 is netural element of addition} -\} \\
  133. &\quad get \gg put \circ (+0)
  134. \end{align*}
  135. and the induction case
  136. \begin{align*}
  137. ...
  138. \end{align*}
  139. \section{Algebraic Effects in Haskell}
  140. % [✓] interface & types with example
  141. % [x] implementation without liftP
  142. % (point out that for an interpreter you might not get a monad which
  143. % is important for a monad)
  144. % [x] handler implementation
  145. % [x] another effect handler
  146. % [x] lifting with an added effect
  147. % [x] handler implementation
  148. Following Brady's \emph{Idris} implementation~\cite{brady} effectful
  149. computations are introduced as a monadic DSL with four primary operations
  150. % FIXME {e ': es => e : es}
  151. > return :: a -> Eff m es r a
  152. > (>>=) :: Eff m es r a -> (a -> Eff m es r b)
  153. > -> Eff m es r b
  154. > mkEffectP :: Elem e es -> e a -> Eff m es r a
  155. > new :: (forall a. Handler e m a) -> Res e
  156. > -> Eff m (e : es) r a -> Eff m es r a
  157. where $return$ and $\bind$ have the obvious meanings, $mkEffectP$ invokes an
  158. effect currently in scope, and $new$ introduces a new effect in a computation.
  159. Notice that effectful computations of type $a$ returning a value of type $r$
  160. additionally specify in their signature $Eff \; m \; es \; r \; a$ an overall
  161. \emph{context} $m$ within which to run---this could be for example a monad---and
  162. the list of effects required $es$.
  163. To exemplify the use of the above interface consider a stateful function to
  164. compute the $n$th fibbonacci number using a table to lookup previously computed
  165. values. Note the familiar monadic style enjoyed by programs written in the
  166. effects language.
  167. % FIXME {'[State (M.Map Int Int)] => [State (M.Map Int Int)]}
  168. > sfibs :: Int -> Eff m [State (M.Map Int Int)] r Int
  169. > sfibs n | n < 2 = return 1
  170. > sfibs n | otherwise = do
  171. > fibs <- get
  172. > case M.lookup n fibs of
  173. > Just c -> return c
  174. > Nothing -> do
  175. > a <- sfibs (n-1)
  176. > b <- sfibs (n-2)
  177. > get >>= put . M.insert n (a + b)
  178. > return (a + b)
  179. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% TODO
  180. \subsection{More stuff}
  181. Additionally the DSL provides $liftP$ as a means for running
  182. computations that require only a subset of the effects currently in scope.
  183. Each effect manages a so called \emph{resource} which can simply be some state
  184. carried through each invocation. Additionally with the introduction of an effect
  185. an associated \emph{handler} must be specified, providing the actual
  186. implementation of an effect's operations.
  187. The effects implementation begins with the introduction of effectful computations
  188. > newtype Eff m es r a = Eff {
  189. > fromEff ::
  190. > (a -> Env m es -> m r) -> Env m es -> m r }
  191. Note that unlike Brady whose effects are evaluated by an interpreter, terms here
  192. are written in direct continuation passing style and it will later be seen that
  193. this simplifies the reasoning process. Here an effectful computation is
  194. contextualized by an environment: $Env \; m \; es$. The purpose of the
  195. environment is two-fold: it tracks the list of effects available to this
  196. computation; and it associates a handler with each effect in scope. Thus the
  197. type of an environment should be expected to follow the shape of its value where
  198. each handler introduced pushes an associated effect onto the effect list.
  199. An environment can then be introduced as a list of handlers and their
  200. associated resources, decorated by the computational context and a list of
  201. effects in scope.
  202. % FIXME {'[] => []} {e ': es => e : es}
  203. > data Env (m :: * -> *) :: [* -> *] -> * where
  204. > Nil :: Env m []
  205. > Cons :: (forall a. Handler e m a) -> Res e
  206. > -> Env m es -> Env m (e : es)
  207. Where the type of a handler is
  208. > type Handler e m a =
  209. > forall t. e t -> (t -> Res e -> m a) -> Res e -> m a
  210. Thus a handler maps effects of type $e \; t$ given a continuation that takes a
  211. result and an updated resource; and the current value of this effect's
  212. associated resource.
  213. This is all the machinery required to implement the effects language.
  214. \subsection{Implementing the Effects Combinators}
  215. Referring back to the signature for an effect, the reader may expect for
  216. $Eff$ to form a monad. Inspecting more closely, notice that $Eff$ is
  217. simply the composition of the $Codensity$ and $Reader$ monad transformers
  218. and thus trivially forms a monad itself.
  219. Introducing the first two operations exposed by the effects language,
  220. the definitions of $return$ and $bind$ can now be given as
  221. > instance Monad (Eff m es r) where
  222. > return a = Eff $ \k -> k a
  223. > Eff m >>= f = Eff $ \k ->
  224. > m (\a -> fromEff (f a) k)
  225. Here $return$ simply yields its value to the rest of the computation.
  226. To implement $bind$ the result of an effectful computation $m$ is passed
  227. into $f$ and the the resulting computation is run.
  228. Recall that the fixed set of operations exposed by an effect are invoked
  229. by $mkEffectP$, meaning to execute an operation the computation is obliged
  230. to prove the associated effect is in scope. Such a proof can be given as an
  231. object of the list membership type
  232. % FIXME {e ': es => e : es} {e' ': es => e' : es}
  233. > data Elem e es where
  234. > Here :: Elem e (e : es)
  235. > There :: Elem e es -> Elem e (e' : es)
  236. Respectively these cases can be taken to mean that either an element is a
  237. member because it is found at the top of a list; or if an element is a member
  238. of some list $es$ it is also a list of a superset of $es$.
  239. Equipped with the above $mkEffectP$ can be given
  240. > mkEffectP :: Elem e es -> e a -> Eff m es r a
  241. > mkEffectP p e = Eff $ execEff p e
  242. where $execEff$ simply looks up and excutes the handler associated with the
  243. effect $e$ in the current environment. By case analysis on $p$
  244. > execEff :: Elem e es -> e a
  245. > -> (a -> Env m es -> m t) -> Env m es -> m t
  246. > execEff Here eff k (Cons handle res env) =
  247. > handle eff (\v res' -> k v (Cons handle res' env)) res
  248. > execEff (There i) eff k (Cons handle res env) =
  249. > execEff i eff
  250. > (\v env' -> k v (Cons handle res env')) env
  251. Given that to call an operation its effect must be in scope, a convenient
  252. method of introducing new effects should be available. This is the purpose
  253. of $new$, which creates a new effect with its initial resource by wrapping
  254. a computation by a handler for its associated operations
  255. % FIXME {e ': es => e : es}
  256. > new :: (forall a. Handler e m a) -> Res e
  257. > -> Eff m (e : es) r a -> Eff m es r a
  258. > new handle r (Eff eff) = Eff $ \k env ->
  259. > eff (\v (Cons handle _ env') -> k v env')
  260. > (Cons handle r env)
  261. First the computation $eff$ is run in an environment extended with the new effect
  262. type, following the newly introduced effect is dropped from the resulting
  263. environment, and the return value is passed into the remaining continuation.
  264. \subsection{Examples}
  265. \subsection{Combining Effectful Computations}
  266. \section{Reasoning about Effects}
  267. \section{Conclusion}
  268. \section{Future Work}
  269. \section*{References}
  270. %@INCLUDE_BIBLIOGRAPHY@
  271. \end{document}