Skip to content

Instantly share code, notes, and snippets.

@gatlin
Last active January 19, 2016 20:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gatlin/af416c142ffb3321d307 to your computer and use it in GitHub Desktop.
Save gatlin/af416c142ffb3321d307 to your computer and use it in GitHub Desktop.
Alternate implementation of the code at http://haskellexists.blogspot.de/2016/01/frp-for-free.html, using the `free` package
import Control.Applicative
import Data.Time (UTCTime, getCurrentTime)
import Control.Monad.Trans.Free
import Control.Comonad
import Control.Comonad.Trans.Cofree
import Data.Functor.Identity
type Next = IO
type BehaviorT = CofreeT Next
type Behavior = BehaviorT Identity
now :: Behavior a -> a
now = extract
future :: Behavior a -> Next (Behavior a)
future = unwrap
type EventT = FreeT Next
type Event = EventT Identity
processEvent ev pr_f fr_f= case runIdentity . runFreeT $ ev of
Pure a -> pr_f a
Free b -> fr_f b
occurred :: a -> Event a
occurred = return
later :: Next (Event a) -> Event a
later = wrap
always :: a -> Behavior a
always a = cofree $ a :< pure (always a)
never :: Event a
never = later (pure never)
sample
:: Event a
-> Behavior b
-> Event (a, b)
sample ev bh = processEvent ev
(\a -> occurred (a, now bh))
(\nextEvent -> later $ sample <$> nextEvent <*> future bh)
plan :: Event (Next a) -> Next (Event a)
plan ev = processEvent ev
(\a -> occurred <$> a)
(\ev' -> (later . plan) <$> ev')
poll :: Behavior (Next a) -> Next (Behavior a)
poll bh =
liftA2 andThen
(now bh)
(poll <$> future bh)
where x `andThen` y = cofree $ x :< y
runNext :: Next a -> IO a
runNext = id
runEvent :: Event a -> IO a
runEvent ev = processEvent ev return $ \nextEvent -> do
event' <- runNext nextEvent
runEvent event'
syncIO :: IO a -> Next a
syncIO = id
currentTime :: Next (Behavior UTCTime)
currentTime = poll $ always $ syncIO getCurrentTime
nextLine :: Next (Event String)
nextLine = plan $ occurred $ syncIO getLine
loop :: Event String -> Behavior UTCTime -> Event ()
loop ev bh = processEvent ev quit_condition continue where
quit_condition msg
| msg == "exit" = occurred ()
| otherwise = later $
syncIO (putStrLn (show (now bh) ++ ": " ++ msg)) *>
(loop <$> nextLine <*> future bh)
continue nextEvent = later $ loop <$> nextEvent <*> future bh
main :: IO ()
main = runEvent $ later $ loop <$> nextLine <*> currentTime
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment