Reputation: 89143
I just reinvented some monad, but I'm not sure which. It lets you model steps of a computation, so you can interleave the steps of numerous computations to find which one finishes first.
{-# LANGUAGE ExistentialQuantification #-}
module Computation where
-- model the steps of a computation
data Computation a = forall b. Step b (b -> Computation a) | Done a
instance Monad Computation where
(Step b g) >>= f = Step b $ (>>=f) . g
(Done b) >>= f = Step b f
return = Done
runComputation :: Computation a -> a
runComputation (Step b g) = runComputation (g b)
runComputation (Done a) = a
isDone :: Computation a -> Bool
isDone (Done _) = True
isDone _ = False
-- an order for a set of computations
data Schedule a = a :> Computation (Schedule a) | Last
toList :: Schedule a -> [a]
toList Last = []
toList (a :> c) = a : (toList . runComputation) c
-- given a set of computations, find a schedule to generate all their results
type Strategy a = [Computation a] -> Computation (Schedule a)
-- schedule all the completed computations, and step the rest,
-- passing the remaining to the given function
scheduleOrStep :: (Queue (Computation a) -> Computation (Schedule a)) -> Strategy a
scheduleOrStep s cs = scheduleOrStep' id cs
where scheduleOrStep' q ((Done a):cs) = Done $ a :> scheduleOrStep' q cs
scheduleOrStep' q ((Step b g):cs) = scheduleOrStep' (q . (g b:)) cs
scheduleOrStep' q [] = s q
-- schedule all completed compuations, step all the rest once, and repeat
-- (may never complete for infinite lists)
-- checking each row of
-- [ [ c0s0, c1s0, c2s0, ... ]
-- , [ c0s1, c1s1, c2s1, ... ]
-- , [ c0s2, c1s2, c2s2, ... ]
-- ...
-- ]
-- (where cNsM is computation N stepped M times)
fair :: Strategy a
fair [] = Done Last
fair cs = scheduleOrStep (fair . ($[])) cs
-- schedule more steps for earlier computations rather than later computations
-- (works on infinite lists)
-- checking the sw-ne diagonals of
-- [ [ c0s0, c1s0, c2s0, ... ]
-- , [ c0s1, c1s1, c2s1, ... ]
-- , [ c0s2, c1s2, c2s2, ... ]
-- ...
-- ]
-- (where cNsM is computation N stepped M times)
diag :: Enqueue (Computation a)-> Strategy a
diag _ [] = Done Last
diag enq cs = diag' cs id
where diag' (c:cs) q = scheduleOrStep (diag' cs) (enq c q $ [])
diag' [] q = fair (q [])
-- diagonal downwards :
-- [ c0s0,
-- c1s0, c0s1,
-- c2s0, c1s1, c0s2,
-- ...
-- cNs0, c{N-1}s1, ..., c1s{N-1}, c0sN,
-- ...
-- ]
diagd :: Strategy a
diagd = diag prepend
-- diagonal upwards :
-- [ c0s0,
-- c0s1, c1s0,
-- c0s2, c1s1, c2s0,
-- ...
-- c0sN, c1s{N-1}, ..., c{s1N-1}, cNs0,
-- ...
-- ]
diagu :: Strategy a
diagu = diag append
-- a queue type
type Queue a = [a] -> [a]
type Enqueue a = a -> Queue a -> Queue a
append :: Enqueue a
append x q = q . (x:)
prepend :: Enqueue a
prepend x q = (x:) . q
I feel like this is probably some kind of threading monad?
Upvotes: 27
Views: 1370
Reputation: 2224
This is (isomorphic to) the delay monad, by co-yoneda:
data Computation a = forall b. Step b (b -> Computation a) | Done a
data Computation a = Step (Coyoneda Identity (Computation a)) | Done a
data Computation a = Step (Identity (Computation a)) | Done a
data Computation a = Step (Computation a) | Done a
EDIT: well, they're isomorphic as endofunctors. Your Monad
instance isn't lawful because return x >>= f ≠ f x
, so there's not much point commenting on that.
Upvotes: 1
Reputation: 4493
It looks like a resumption-with-state monad. I think there used to be a resumption monad in MTL around GHC 6.6 but if there was it disappeared. William L. Harrison has a number of papers about resumption monads, including Cheap (But Functional) Threads and The Essence of Multitasking.
Upvotes: 5
Reputation: 133
Note the Free monad construction
data Free f a = Pure a | Free (f (Free f a))
Therefore, we can write
data ComputationF a = forall b. ComputationF b (b -> a)
type Computation = Free ComputationF
Upvotes: 2
Reputation: 13793
I don't understand why not
data Computation a = Step (Computation a) | Done a
instance Monad Computation where
(Step g) >>= f = Step $ g >>= f
(Done b) >>= f = Step (f b)
return = Done
I'm not sure what this monad is, but it's definitely simpler and seems to be equivalent in most respects.
Upvotes: 5
Reputation: 907
This looks similar to the definition of stream fusion used by Don Stewart did a while ago, and also somewhat related to iteratees (though wihtout the notion of pushing data into the iteratee using an enumerator), but less so than stream fusion I guess.
Upvotes: 2
Reputation: 2297
I haven't spent too much time understanding your code, but it really sounds like the coroutine monad from the monad-coroutine package, which may be a bit more general.
Upvotes: 2