Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
reflex-frp
GitHub Repository: reflex-frp/reflex-platform
Path: blob/develop/examples/host.hs
1 views
1
{-# LANGUAGE MultiParamTypeClasses #-}
2
{-# LANGUAGE RankNTypes #-}
3
4
{-|
5
6
This program gives an example of how to construct a simple framework that
7
allows programmers to write FRP applications that process keystrokes (of
8
type Char) to produce a view (of type String). This model, where programmers
9
process Event(s) to produce Behavior(s) is akin to the OpenGL/GLUT model, where
10
input is pushed into the application via callbacks and and output is pulled
11
via periodic sampling in a render function. Other application models work
12
differently; for example, DOM applications built with reflex-dom ultimately
13
produce Events rather than Behaviors as their output, since changes need to be
14
pushed (rather than pulled) into the web browser's DOM hierarchy. The precise
15
mix of input and output structures required will depend on the needs of the
16
external systems with which the FRP-enabled program is interacting.
17
18
-}
19
20
module Main where
21
22
import Reflex
23
import Reflex.Host.Class (newEventWithTriggerRef, runHostFrame, fireEvents)
24
import Control.Concurrent (forkIO)
25
import Control.Monad (forever)
26
import Control.Monad.Fix (MonadFix)
27
import Control.Monad.Identity (Identity(..))
28
import Control.Monad.IO.Class (liftIO)
29
import Data.IORef (readIORef)
30
import Data.Dependent.Sum (DSum ((:=>)))
31
import System.IO (hSetEcho, hSetBuffering, stdin, BufferMode (NoBuffering))
32
33
-- | Define the type for apps using our host framework. Programmers
34
-- will write programs of type @TypingApp t m@ and use our
35
-- framework to run them.
36
--
37
-- In this framework, the user will write programs that take an input
38
-- event representing keystrokes and produce an output behavior representing
39
-- the current view to be shown. This is similar to how polling-driven
40
-- output frameworks such as OpenGL will work.
41
type TypingApp t m = (Reflex t, MonadHold t m, MonadFix m)
42
=> Event t Char
43
-> m (Behavior t String)
44
45
-- | Run a program written in the framework. This will do all the necessary
46
-- work to integrate the Reflex-based guest program with the outside world
47
-- via IO.
48
host :: (forall t m. TypingApp t m)
49
-- ^ By keeping t and m abstract, we ensure that the user (the
50
-- programmer using our framework) can't make any assumptions
51
-- about which Reflex implementation is being used
52
-> IO ()
53
host myGuest =
54
55
-- Use the Spider implementation of Reflex.
56
runSpiderHost $ do
57
58
-- Create an event to be used as input.
59
-- It will fire wehenver we use eTriggerRef.
60
(e, eTriggerRef) <- newEventWithTriggerRef
61
62
-- Evaluate our user's program to set up the data flow graph.
63
-- This usually only needs to be done once; the user can change the data
64
-- flow graph arbitrarily in response to events.
65
--
66
-- runHostFrame is an efficient way of running a computation that
67
-- can build arbitrary data flow graphs using 'hold' and 'sample'.
68
--
69
-- (The pure combinators in the Reflex class can be used in any context,
70
-- so they don't need any special treatment - but inside runHostFrame is
71
-- as good a place as any to run them.)
72
b <- runHostFrame $ myGuest e
73
74
-- Begin our event processing loop.
75
forever $ do
76
77
-- Get an input event and display it.
78
input <- liftIO getChar
79
liftIO $ putStrLn $ "Input Event: " ++ show input
80
81
-- Retrieve the current event trigger.
82
mETrigger <- liftIO $ readIORef eTriggerRef
83
84
-- Use the trigger to deliver the event.
85
case mETrigger of
86
Nothing ->
87
-- This means that nobody is subscribed to the input event.
88
--
89
-- Since this is the only input event in this system, that would
90
-- mean the guest program must be really boring! However, in larger
91
-- programs, there are often many input events, and most programs
92
-- will not care about every single one of them.
93
--
94
-- Note: The missing trigger does NOT mean we should buffer the
95
-- input and deliver it later - it means that nobody is interested
96
-- in this occurrence, so we should discard it.
97
return ()
98
Just eTrigger ->
99
-- We have a trigger, so someone is interested in this input event
100
-- occurrence.
101
--
102
-- fireEvents will process an event frame to deliver the event to
103
-- anyone in the data flow graph who is interested in it. It can
104
-- also deliver multiple simultaneous events if necessary. However,
105
-- the same event cannot be firing multiple times simultaneously;
106
-- system behavior is undefined if the same trigger is provided more
107
-- than once.
108
fireEvents [eTrigger :=> Identity input]
109
110
-- Retrieve the current output of the user's program and display it.
111
output <- runHostFrame $ sample b
112
liftIO $ putStrLn $ "Output Behavior: " ++ show output
113
114
-- | This is a simple guest program written with our framework. It just
115
-- accumulates all the characters that the user has typed.
116
-- Backspace functionality is left as an exercise for the reader.
117
guest :: TypingApp t m
118
guest e = do
119
120
-- Accumulate the input events in a list.
121
-- Each one represents a keypress from the end user.
122
d <- foldDyn (:) [] e
123
124
-- Since we're using cons to accumulate keystrokes, they will end up in
125
-- reverse order. Use `reverse` to fix that.
126
return $ fmap reverse $ current d
127
128
-- | Main is just doing some setup so that the program's output will look nice,
129
-- and then invoking `host`.
130
main :: IO ()
131
main = do
132
putStrLn "Welcome to the example Reflex host app; press Ctrl+C to exit"
133
putStrLn "Press any key to process it with the Reflex FRP engine"
134
135
-- Prevent the user's input from showing up until we want it to.
136
hSetEcho stdin False
137
138
-- Ensure that we process each character right away, instead of waiting
139
-- until the user presses enter.
140
hSetBuffering stdin NoBuffering
141
142
-- Run the guest program using our host framework.
143
host guest
144
145