on master help sign in
streamly/docs/User/Tutorials/ReactiveProgramming.hs
126 lines 5.0 kB history raw
1{-# OPTIONS_GHC -fno-warn-unused-imports #-}
2-- |
3-- Module : User.Tutorials.ReactiveProgramming
4-- Copyright : (c) 2017 Composewell Technologies
5--
6-- License : BSD3
7-- Maintainer : streamly@composewell.com
8--
9-- THIS TUTORIAL IS OBSOLETE.
10--
11-- In this tutorial we will show how Streamly can be used for reactive
12-- programming. Before you go through this tutorial we recommend that you take
13-- a look at the Streamly concurrent programming tutorial.
15module User.Tutorials.ReactiveProgramming
16 (
17 -- * Reactive Programming
18 -- $reactive
20 -- * Where to go next?
21 -- $furtherReading
22 )
23where
25import Streamly.Data.Stream
26import Data.Semigroup
27import Control.Applicative
28import Control.Monad
29import Control.Monad.IO.Class (MonadIO(..))
30import Control.Monad.Trans.Class (MonadTrans (lift))
32-- $reactive
33--
34-- Reactive programming is nothing but concurrent streaming which is what
35-- streamly is all about. With streamly we can generate streams of events,
36-- merge streams that are generated concurrently and process events
37-- concurrently. We can do all this without any knowledge about the specifics
38-- of the implementation of concurrency. In the following example you will see
39-- that the code is just regular Haskell code without much streamly APIs used
40-- (active hyperlinks are the streamly APIs) and yet it is a reactive
41-- application.
42--
43-- This application has two independent and concurrent sources of event
44-- streams, @acidRain@ and @userAction@. @acidRain@ continuously generates
45-- events that deteriorate the health of the character in the game.
46-- @userAction@ can be "potion" or "quit". When the user types "potion" the
47-- health improves and the game continues.
48--
49-- @
50-- {-\# LANGUAGE FlexibleContexts \#-}
51--
52-- import "Streamly.Prelude" (MonadAsync, SerialT)
53-- import "Streamly.Prelude" as Stream
54-- import Control.Monad (void)
55-- import Control.Monad.IO.Class (MonadIO(liftIO))
56-- import Control.Monad.State (MonadState, get, modify, runStateT)
57--
58-- data Event = Quit | Harm Int | Heal Int deriving (Show)
59--
60-- userAction :: MonadAsync m => 'SerialT' m Event
61-- userAction = Stream.'repeatM' $ liftIO askUser
62-- where
63-- askUser = do
64-- command <- getLine
65-- case command of
66-- "potion" -> return (Heal 10)
67-- "harm" -> return (Harm 10)
68-- "quit" -> return Quit
69-- _ -> putStrLn "Type potion or harm or quit" >> askUser
70--
71-- acidRain :: MonadAsync m => 'SerialT' m Event
72-- acidRain = Stream.'fromAsync' $ Stream.'constRate' 1 $ Stream.'repeatM' $ liftIO $ return $ Harm 1
73--
74-- data Result = Check | Done
75--
76-- runEvents :: (MonadAsync m, MonadState Int m) => 'SerialT' m Result
77-- runEvents = do
78-- event \<- userAction \`Stream.'parallel'` acidRain
79-- case event of
80-- Harm n -> modify (\\h -> h - n) >> return Check
81-- Heal n -> modify (\\h -> h + n) >> return Check
82-- Quit -> return Done
83--
84-- data Status = Alive | GameOver deriving Eq
85--
86-- getStatus :: (MonadAsync m, MonadState Int m) => Result -> m Status
87-- getStatus result =
88-- case result of
89-- Done -> liftIO $ putStrLn "You quit!" >> return GameOver
90-- Check -> do
91-- h <- get
92-- liftIO $ if (h <= 0)
93-- then putStrLn "You die!" >> return GameOver
94-- else putStrLn ("Health = " <> show h) >> return Alive
95--
96-- main :: IO ()
97-- main = do
98-- putStrLn "Your health is deteriorating due to acid rain, type \\\"potion\\\" or \\\"quit\\\""
99-- let runGame = Stream.'drainWhile' (== Alive) $ Stream.'mapM' getStatus runEvents
100-- void $ runStateT runGame 60
101-- @
102--
103-- You can also find the source of this example in the streamly-examples repo
104-- as <https://github.com/composewell/streamly-examples/tree/master/AcidRain.hs AcidRain.hs>.
105-- It has been adapted from Gabriel's
106-- <https://hackage.haskell.org/package/pipes-concurrency-2.0.8/docs/Pipes-Concurrent-Tutorial.html pipes-concurrency>
107-- package.
108-- This is much simpler compared to the pipes version because of the builtin
109-- concurrency in streamly. You can also find a SDL based reactive programming
110-- example adapted from Yampa in
111-- <https://github.com/composewell/streamly-examples/tree/master/CirclingSquare.hs CirclingSquare.hs>.
113-- $performance
114--
115-- Streamly is highly optimized for performance, it is designed for serious
116-- high performing, concurrent and scalable applications. We have created the
117-- <https://hackage.haskell.org/package/streaming-benchmarks streaming-benchmarks>
118-- package which is specifically and carefully designed to measure the
119-- performance of Haskell streaming libraries fairly and squarely in the right
120-- way. Streamly performs at par or even better than most streaming libraries
121-- for serial operations even though it needs to deal with the concurrency
122-- capability.
124-- $furtherReading
125--
126-- * See the examples in <https://github.com/composewell/streamly-examples streamly-examples> repo.