Last active
September 19, 2017 23:37
-
-
Save gatlin/3fe7367e661f71199e74 to your computer and use it in GitHub Desktop.
yeah I'm thinking about Orc again
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{- | |
This came from looking at the source for the orc package on hackage. | |
I wanted to know why it works, and to see if I can come up with an | |
alternative implementation which does cool things, and possibly provide | |
a mechanism for orchestrating non-linear tube computations. | |
-} | |
-- | Pilfered from the orc package on Hackage. | |
-- The original is specialized to one MonadIO instance. | |
-- Also this looks suspiciously like a continuation monad. | |
newtype Orc m a = Orc { (#) :: (a -> m ()) -> m () } | |
instance MonadIO m => Functor (Orc m) where | |
fmap f orc = Orc $ \k -> orc # (k . f) | |
instance MonadIO m => Applicative (Orc m) where | |
pure x = Orc $ \k -> k x | |
orcF <*> orcA = Orc (\k -> | |
orcF # (\f -> | |
orcA # (\a -> k (f a)))) | |
instance MonadIO m => Monad (Orc m) where | |
return = pure | |
-- back in my day monads were triples of map, unit, and join tyvm | |
ma >>= f = _join (fmap f ma) where | |
_join orc = Orc $ \k -> orc # (\x -> x # k) | |
instance MonadTrans Orc where | |
lift m = Orc (m >>=) | |
evalOrc :: MonadIO m => Orc m () -> m () | |
evalOrc orc = orc # return | |
-- | Signal the cessation of an Orc computation. | |
stop :: MonadIO m => Orc m a | |
stop = Orc $ \_ -> return () | |
-- | Hey I was right Orc is a continuation monad! | |
callCC :: MonadIO m => ((a -> Orc m b) -> Orc m a) -> Orc m a | |
callCC f = Orc $ \k -> (f (\x -> Orc $ \_ -> k x)) # k | |
-- 'callCC' demonstration. Kind of a tangent but it hints at the | |
-- deeper relationship between continuations and concurrency. | |
whatsYourName :: String -> IO () | |
whatsYourName name = | |
(# id) $ do | |
response <- callCC $ \exit -> do | |
validateName name exit | |
return $ "Welcome, " ++ name | |
return $ putStrLn $ "response: " ++ response | |
validateName :: (Applicative f, Foldable t) | |
=> t a -> (String -> f ()) -> f () | |
validateName name exit = when (null name) (exit "fuck you") | |
-- ohay look delimited continuations, too | |
reset :: MonadIO m => Orc m () -> Orc m () | |
reset = lift . evalOrc | |
shift :: MonadIO m => ((a -> m ()) -> Orc m ()) -> Orc m a | |
shift f = Orc (evalOrc . f) | |
{- * TODO | |
The following primitives must be implemented somehow. My strong suspicion is | |
that tubes will play a role in this, as they are both continuations and streams | |
returning more than once. | |
Additionally, oleg has shown that something like | |
Tube a b (Tube a b m) () | |
can effectively yield results asynchronously. Well, it permits asynchronous results | |
to be passed along as they come; ultimately asynchronicity is dependent on the runtime. | |
The function wrapped up inside | |
`Orc` needs only to produce the specified function - so taking an existing function | |
and using arbitrarily nested tubes is fine as long as the final function has that | |
signature. | |
-- | Parallel choice operator with no left or right bias. Returns | |
-- results as they become available. | |
par :: MonadIO m => Orc m a -> Orc m a -> Orc m a | |
Current implementation in the package: | |
par :: Orc a -> Orc a -> Orc a | |
par p q = Orc $ \k -> do | |
fork (p # k) | |
q # k | |
This *really* looks like | |
someNestedTube p q = do | |
lift p | |
q | |
-- | Biased choice operator. Seems like it's the same as (>>) ... | |
(<+>) :: MonadIO m => Orc m a -> Orc m a | |
Current implementation: | |
p <+> q = Orc $ \k -> do | |
w <- newGroup | |
local w $ fork (p # k) | |
finished w | |
q # k | |
{- | | |
Immediately fires up a thread for p, and then returns a handle to the first | |
result of that thread which is also of type Orc a. An invocation to eagerly | |
is non-blocking, while an invocation of the resulting handle is blocking. | |
-} | |
eagerly :: MonadIO m => Orc m a -> Orc m (Orc m a) | |
Current implementation: | |
eagerly p = Orc $ \k -> do | |
res <- newEmptyMVar | |
w <- newGroup | |
local w $ fork (p `saveOnce` (res,w)) | |
k (liftIO $ readMVar res) | |
saveOnce :: Orc a -> (MVar a, Group) -> HIO () | |
p `saveOnce` (r,w) = do | |
ticket <- newMVar () | |
p # \x -> (takeMVar ticket >> putMVar r x >> close w) | |
-- | Executes an orc computation, returns the first result, and discards the rest. | |
cut :: MonadIO m => Orc m a -> Orc m a | |
cut = join . eagerly | |
-} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment