-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathHost2.hs
141 lines (119 loc) · 4.44 KB
/
Host2.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
{-
Copyright : (c) Dave Laing, 2016
License : BSD3
Maintainer : [email protected]
Stability : experimental
Portability : non-portable
-}
{-# LANGUAGE RankNTypes #-}
module Host2 (
go2
) where
import Data.Maybe (isJust)
import Control.Monad (unless)
import Control.Monad.Identity (Identity(..))
import Control.Monad.IO.Class (liftIO)
import Data.IORef (readIORef)
import System.IO
import Data.Dependent.Sum
import Reflex
import Reflex.Host.Class
-- I'm going to assume that you've read through Host1.hs prior to this.
-- We are going to update the type of our applications.
--
-- Previously we had a 'Behavior t Int' as an output, and now we have
-- an 'Event t ()' as an output.
--
-- In this case we're going to use that event to signal when the
-- application wants to stop, so that we can exit cleanly.
type SampleApp2 t m =
( Reflex t
, MonadHold t m
) => Event t String
-> m (Event t ())
-- This is our sample application.
--
-- Every time our input 'Event t String' fires, we're going to check
-- to see if the 'String' value is "/quit".
--
-- We return an event that fires when this is the case.
--
-- It's boring for now, but we'll build on it.
guest :: SampleApp2 t m
guest e = do
let
eQuit = () <$ ffilter (== "/quit") e
return eQuit
-- This is the code that runs our FRP applications.
host :: (forall t m. SampleApp2 t m)
-> IO ()
host myGuest =
-- We use the Spider implementation of Reflex.
runSpiderHost $ do
-- We create a new event and a trigger for the event.
(e, eTriggerRef) <- newEventWithTriggerRef
-- We set up our basic event network to use with 'myGuest e'.
eQuit <- runHostFrame $ myGuest e
-- eQuit :: Event t ()
-- This gives us an 'Event t ()' which signals the intent to quit.
-- We want to be able to work out when that event has fired, so
-- we subscribe to the event.
hQuit <- subscribeEvent eQuit
-- hQuit :: EventHandle t ()
--
-- This gives us an event handle, which we can use to read
-- our output events.
-- A little bit of set up:
liftIO $ hSetBuffering stdin LineBuffering
-- We define our main loop.
--
-- We're not using 'forever' anymore, because we want to be
-- able to exit cleanly from this loop.
let
loop = do
-- We get a line from stdin
input <- liftIO getLine
-- and we print it out for debugging purposes
liftIO $ putStrLn $ "Input Event: " ++ show input
-- We read the event trigger
mETrigger <- liftIO $ readIORef eTriggerRef
mQuit <- case mETrigger of
-- If no one is listening, we do nothing
Nothing -> do
return Nothing
-- If there is someone listening, we fire our input
-- events and read from the output events.
Just eTrigger -> do
-- The firing of the events happens as usual, except:
-- fireEventsAndRead :: [DSum (EventTrigger t) Identity] -> ReadPhase m a -> m a
fireEventsAndRead [eTrigger :=> Identity input] $ do
-- we now have a read phase that happens after the events have been fired.
-- The main thing that we do in the 'ReadPhase' is call 'readEvent' and
-- deal with its output.
-- The event may not be occurring, so there's a 'Maybe' in there:
-- readEvent :: EventHandle t a -> m (Maybe (m a))
mValue <- readEvent hQuit
-- and we shuffle this into a form that we can use with 'sequence':
sequence mValue
-- Again, there is a helper functions that reads the trigger
-- reference, fires the trigger if it is not 'Nothing', and then
-- reads an output event from a particular event handle.
--
-- The above block could be replaced with:
-- mQuit <- fireEventRefAndRead eTriggerRef input hQuit
-- The result of this block is
-- mQuit :: Maybe ()
-- which has filtered up through a few layers to get to us, but is still
-- perfectly serviceable.
-- We print out the value for debugging purposes:
liftIO $ putStrLn $ "Output Event: " ++ show mQuit
-- and then use it to determine if we'll continue with the loop:
unless (isJust mQuit)
loop
-- This starts the actual loop
loop
-- Now we can run our sample application ('guest') using
-- our code for hosting this kind of applications ('host').
go2 :: IO ()
go2 =
host guest