{- | An EDSL to make working with concurrent in-process code a bit easier to read. This module is expected to be imported qualified as `Go`. Inspired by Golang and Clojure's core.async. $example -} {-# LANGUAGE NoImplicitPrelude #-} module Com.Simatime.Go ( -- * Running and forking Go , run , fork -- * Channels , Channel , chan , broadcast , tap , read , write ) where import Control.Concurrent ( forkIO , ThreadId ) import qualified Control.Concurrent.STM.TChan as TChan import GHC.Conc ( STM , atomically ) import Protolude ( IO ) type Go = STM type Channel = TChan.TChan -- | Runs a Go command in IO. run :: Go a -> IO a run = atomically -- | Starts a background process. fork :: IO () -> IO ThreadId fork = forkIO -- | Make a new channel. chan :: Go (Channel a) chan = TChan.newTChan -- | Make a read-only channel. broadcast :: Go (Channel a) broadcast = TChan.newBroadcastTChan -- | Duplicates a channel, but then anything written to the source will -- be available to both. This is like a combination of Clojure's -- `core.async/mult` and `core.async/tap` but. tap :: Channel a -> Go (Channel a) tap = TChan.dupTChan -- | Take from a channel. Blocks until a value is received. read :: Channel a -> Go a read = TChan.readTChan -- | Write to a channel. write :: Channel a -> a -> Go () write = TChan.writeTChan {- $example A simple example from ghci: >>> import qualified Com.Simatime.Go as Go >>> c <- Go.run Go.chan :: IO (Go.Channel Text) >>> Go.run $ Go.write c "test" >>> Go.run $ Go.read c "test" -}