Skip to content

Commit be82748

Browse files
committed
Small doc updates
1 parent 2a55cdf commit be82748

1 file changed

Lines changed: 42 additions & 43 deletions

File tree

src/Theseus/EffType.hs

Lines changed: 42 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
{-# LANGUAGE DeepSubsumption #-}
2-
{-# LANGUAGE FunctionalDependencies #-}
31
{-# LANGUAGE QuantifiedConstraints #-}
42

53
module Theseus.EffType where
@@ -35,9 +33,9 @@ import Theseus.Union
3533
--
3634
-- One really cool property of this `Freer` is how the `f` never needs to
3735
-- change until it's ready to be interpreted. We drop the `f` into the `Impure`
38-
-- constructor, and until we're ready to interpret the value stays the same.
36+
-- constructor, and, until we're ready to interpret, the value stays the same.
3937
-- This is different from the `Free` monad where every `bind` operation means
40-
-- calling `map` on the `f` to change it. Not having to change `f` before
38+
-- calling `map` on `f`. Not having to change `f` before
4139
-- interpretation is cool because it means we make fewer assumptions about
4240
-- `f`'s structure. At interpretation time we know exactly what `f` is, so it's
4341
-- easy to manipulate. During operations like `bind`, we don't know exactly
@@ -74,7 +72,7 @@ import Theseus.Union
7472
-- back, but it makes things awkward in a different way. One of the cool things
7573
-- about `Freer` compared to `Free` was how we avoided calling `map` on the
7674
-- `f`s. Now we've kind of added that back by requiring all our `f`s be
77-
-- HFunctors.
75+
-- `HFunctor`s.
7876
--
7977
-- If `Freer` gets around the `map` problem by introducing a continuation and an
8078
-- existential variable, can we do something similar to get around the `hmap`
@@ -150,38 +148,38 @@ import Theseus.Union
150148
--
151149
-- The `CF` type gives us enough information to implement things like
152150
-- `runThrow`, `runCoroutine`, and `runChoice` without dropping or duplicating
153-
-- code that frees resources.
151+
-- code that frees resources. Consumers of `CF` might represent a single thread
152+
-- of computation, multiple threads of computation, a paused computation, an
153+
-- aborted computation, a computation that needs to do some other computations
154+
-- on the side, etc. Some of these let you move beyond scoped effects.
155+
-- Consumers look like catamorphisms/folds.
154156
--
155-
-- Consumers of `CF` might represent a single thread of computation, multiple
156-
-- threads of computation, a paused computation, an aborted computation,
157-
-- a computation that needs to do some other computations on the side, etc.
158-
-- Some of these let you move beyond scoped effects. Consumers look like
159-
-- catamorphisms/folds.
160-
--
161-
-- The first two operations in `CF` allow you to add more computations to the
162-
-- flow. The interpreter decides whether to duplicate those between multiple
163-
-- threads, drop them, or something else. Those operations are required for
164-
-- `Freer` to be a Monad and Applicative.
157+
-- The first constructor for `CF` is the base case for our tree-like structure.
158+
-- It tells us that the Control Flow simply returns its input. The second
159+
-- allows you to add more computations to the flow. The interpreter decides
160+
-- whether to duplicate those between multiple threads, drop them, or something
161+
-- else.
165162
--
166163
-- The last constructor is the really tricky one. Unfortunately the type
167164
-- signature above is far too restrictive for a lot of useful
168-
-- `interpretations`s. For example, imagine an interpreter or catch which
165+
-- interpretations. For example, imagine an interpreter for catch which
169166
-- ignores all the catch blocks and simply returns `Nothing` if anything
170167
-- throws. Although it might sound simpler than the normal `Catch`
171168
-- interpretation, it's actually much trickier. It represents control flow that
172-
-- has at most one running thread. We need the fold to give us an `Freer
173-
-- es (Maybe a)`. When we try to handle `CfRun`, we'll be stuck with a `Freer
174-
-- es (wrap (Maybe a))`. If the `wrap` is completely opaque, there's no way we
175-
-- can get it back into the correct `Freer es (Maybe (wrap a))` shape.
169+
-- has at most one running thread. We need the fold to give us a `Freer es
170+
-- (Maybe a)`. When we try to handle `CfRun` though, we'll be stuck with
171+
-- a `Freer es (wrap (Maybe a))`. If the `wrap` is completely opaque, there's
172+
-- no way we can get it back into the correct `Freer es (Maybe (wrap a))`
173+
-- shape.
176174
--
177175
-- The solution Theseus adopts is to put restrictions on `wrap`. For our weird
178176
-- catch, it's good enough for `wrap` to implement `Traversable`. That means we
179-
-- can turn a `wrap (Maybe a)` into a `Maybe (wrap a)`. It turns out though
180-
-- that some `wrap`s (like the one returned by a `Coroutine` interpreter) don't
181-
-- implement `Traversable`. To support both `ControlFlow`s that require
182-
-- `Traversable` and interpreters which use values that don't, Theseus makes
183-
-- the constraint on `wrap` flexible. When you're looking at the real
184-
-- implementations, that's what all the `lb` and `ub` variables are for.
177+
-- can turn a `wrap (Maybe a)` into a `Maybe (wrap a)` using `sequence`. It
178+
-- turns out though that some `wrap`s (like the one returned by a `Coroutine`
179+
-- interpreter) don't implement `Traversable`. To support both `ControlFlow`s
180+
-- that require `Traversable` and interpreters which use values that don't,
181+
-- Theseus makes the constraint on `wrap` flexible. When you're looking at the
182+
-- real implementations, that's what all the `lb` and `ub` variables are for.
185183
--
186184
-- When you reach the real `CF` type, you'll see much more than the
187185
-- 3 constructors I shared above. Some of them are there to support things like
@@ -211,7 +209,7 @@ type Bound = (Type -> Type) -> Constraint
211209
-- Why? Interpreters want to rely on the fact that the upper bound implies the
212210
-- lower bound. Since `lb` and `ub` are usually type variables, every single
213211
-- function working with `Eff` would need to add or pass around proof that `ub`
214-
-- implies `lb`. To avoid of that noise, we use a `Reader` pattern. You can
212+
-- implies `lb`. To avoid all that noise, we use a `Reader` pattern. You can
215213
-- construct a nonsense type like `Eff Show Read ...`, but you can't run it.
216214
--
217215
-- I might try adding other useful pieces of information to the `Facts` object.
@@ -234,13 +232,14 @@ effUn = flip unEff
234232

235233
-- | A higher order Freer Monad that keeps track of some extra stuff. The type
236234
-- parameters match `Eff`. Usually, a `Freer` type would have just two type
237-
-- parameters and look like `Freer f a` where `f` is some kind of open union
238-
-- type. Instead I hardcode this to use an open union because that simplified
239-
-- some of the other bits.
235+
-- parameters and look like `Freer f a`. Then somewere else you make `f` into
236+
-- some concrete open union type. Instead I hardcode this to use an open union
237+
-- because that simplified some of the other bits. The extensible effects paper
238+
-- also hardcodes, so I think that's fine.
240239
data Freer :: Bound -> Bound -> [Effect] -> Type -> Type where
241240
-- | Pure simply holds a value. You can think of Theseus as transforming
242-
-- `Impures` over and over again until all that's left is a pure value. Once
243-
-- a Freer is a pure value, it will no longer be run. This fact allows
241+
-- `Impures` over and over again until all that's left is a `Pure` value.
242+
-- Once a Freer is a `Pure` value, it will no longer be run. This fact allows
244243
-- finalizers to exist.
245244
Pure :: a -> Freer lb ub es a
246245
-- | Impure holds an effect that needs to be run before the program can
@@ -262,12 +261,12 @@ data Freer :: Bound -> Bound -> [Effect] -> Type -> Type where
262261
-- run. The second proves that the upper bound has only gotten higher as
263262
-- the program has run. Together they prove that our bounding constraints
264263
-- have not contracted. With these proofs, interpreters can know that their
265-
-- type changes are valid, and that other interpreters will use valid type
266-
-- changes. The third parameter is a proof that effects that are in scope
267-
-- when the state is being interpreted are in scope when the effect is
268-
-- being sent. It's convenient when writing interpreters and intuitively
269-
-- makes sense even if it's sometimes not true. All effects that haven't
270-
-- been raised will remain in scope.
264+
-- return type changes are valid, and that other interpreters will use
265+
-- valid return type changes. The third parameter is a proof that effects
266+
-- that are in scope when the state is being interpreted are in scope when
267+
-- the effect is being sent. It's convenient when writing interpreters and
268+
-- intuitively makes sense even if it's sometimes not true. All effects
269+
-- that haven't been raised will remain in scope.
271270
lbSend `Implies` lb ->
272271
ub `Implies` ubSend ->
273272
(forall eff. eff `IsMember` es -> Maybe (eff `IsMember` esSend)) ->
@@ -326,7 +325,7 @@ data CF input f a where
326325
-- fmap operations running one after another. We can detect that and turn
327326
-- them into plain function compositions which GHC optimizes extremely well.
328327
-- In one realistic benchmark, this changes the amount of memory that has to
329-
-- be allocated by an order of magnitude.
328+
-- be allocated by orders of magnitude.
330329
CfFmap :: (a -> b) -> CF input f a -> CF input f b
331330
CfApply :: CF input f (a -> b) -> f a -> CF input f b
332331
CfBind :: CF input f a -> (a -> f b) -> CF input f b
@@ -406,9 +405,9 @@ data CF input f a where
406405
-- Sometimes while handling `CfOnce`, you realize you need to inject your own
407406
-- `CF` handling code while another handler is running. While I feel like
408407
-- `CfOnce` behaves reasonably, this one is much more awkward and destroys
409-
-- information about the running threads. It hasn't come up in practice, but
410-
-- I would like to find improvements. The problematic handlers are in the
411-
-- Choice and Error modules.
408+
-- information about the running threads. That problem hasn't come up in
409+
-- practice yet, but I would like to find improvements. The problematic
410+
-- handlers are in the Choice and Error modules.
412411
CfPutMeIn ::
413412
(forall a. Monoid (k a)) =>
414413
(Eff lbSend ubSend esSend (k a) -> Eff lb ub es (k b)) ->

0 commit comments

Comments
 (0)