-
Notifications
You must be signed in to change notification settings - Fork 5
/
AsyncFunctions.hs
192 lines (164 loc) · 7.81 KB
/
AsyncFunctions.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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
module AsyncFunctions where
-- stack script --resolver lts-8.22
import Control.Concurrent
import Control.Concurrent.Async
import Data.Char
import Data.List
import System.Random
import Control.Monad.IO.Class (liftIO)
newRand = randomRIO (0, 100 :: Int)
-- | Sync operation
-- -----------------------
{-| In do block of Haskell by default operations are done sequentially in this example we can see how
applying some delays in operations finish after that delay time in a sequential order--}
syncOperation :: String -> IO String
syncOperation input = do
delay <- getStdRandom (randomR (1000000,1001000))
_ <- threadDelay delay
threadId <- myThreadId
print ("Running " ++ input ++ " in thread: " ++ show threadId)
return input
-- runs n operations synchronously
sync :: Int -> IO ()
sync 0 = return () -- If we reach 0 this function it will be called
sync number = do
_ <- syncOperation (show number)
sync (pred number)
-- | Async operator
-- ---------------------
{-| With async operator we ensure the operation is executed in another thread.
Here we wait and block for the resolution of the execution until we get the result from the other thread.
We can also of course compose multiple results as we can see in the second code base. |-}
asyncResponse :: IO ()
asyncResponse = do
resAsync <- async getOperation
response <- wait resAsync
print response
multipleAsyncResponse :: IO ()
multipleAsyncResponse = do
resAsync1 <- async getOperation -- Run the operation in a new thread
resAsync2 <- async getOperation3 -- Run the operation in a new thread
response1 <- wait resAsync1 -- Wait for the other thread to finish
response2 <- wait resAsync2 -- Wait for the other thread to finish
print ( map toUpper response1, response2)
{-| Another example here we use also async operators, passing two monads to a function that expect to
receive this two monad strings and then we use the async and wait for the results.-}
combiningAsyncResponse :: IO ()
combiningAsyncResponse = do
response <- getMonadsOperation getOperation getOperation3
print response
getMonadsOperation :: IO String -> IO String -> IO String
getMonadsOperation word1 word2 = let ioWord1 = word1
ioWord2 = word2
in do
asyncResponse1 <- async ioWord1
asyncResponse2 <- async ioWord2
response1 <- wait asyncResponse1
response2 <- wait asyncResponse2
return (response1 ++ " " ++ response2)
{-| It is also possible combine in a do block io monads ans values -}
combiningAsyncResponse1 :: IO ()
combiningAsyncResponse1 = do
resAsync2 <- async getOperation3
response2 <- wait resAsync2
response <- getMonadsOperation1 getOperation response2
print response
getMonadsOperation1 :: IO String -> String -> IO String
getMonadsOperation1 word1 word2 = let ioWord1 = word1
_word2 = word2
in do
asyncResponse1 <- async ioWord1
response1 <- wait asyncResponse1
return (response1 ++ " " ++ _word2)
{-| Having monads we can also use [fmap] operator to transform the value inside the monad -}
fmapNumberAsync :: IO ()
fmapNumberAsync = do
resAsync1 <- async getOperation2 -- Run the operation in a new thread
response1 <- wait (fmap (\number -> number + 100) resAsync1) -- Wait and transform the value that wrap the monad
print response1
{-| In this example using again [fmap] we get the value from the first thread element and we combine in the second one -}
fmapSentenceAsync :: IO ()
fmapSentenceAsync = do
resAsync1 <- async getOperation
resAsync2 <- async getOperation3
response2 <- wait resAsync2
-- Wait and transform the value that wrap the monad
response1 <- wait (fmap (\sentence -> map toUpper sentence ++ " " ++ response2) resAsync1)
print response1
-- | Concurrently operator
-- -----------------------
{-| [Concurrently] allow us execute two operations in parallel, and once we have both of them finish
we can return a tuple of types defined in the actions |-}
concurrentOutput = do
res <- concurrently getOperation2 getOperation3
print (res :: (Int, String))
-- | Race operator
-- -----------------------
{-| [Race] operator is just like the ScalaZ race operator, is running two operations in multiple threads, and the first one
that finish win and the other operation is cancelled.
as a result we receive an [Either] of the two possible types defined in the operations. |-}
raceOutput = do
res <- race getOperation2 getOperation3
print (res :: Either Int String)
-- | ForkIO operator
-- -----------------------
{-| [forkIO] operator is used to run a do block in another thread.
Here we use an empty MVar, we run a new thread with fork and we listen to the MVar for a new value.
Then from the main thread we put a value into the variable and since is subscribed from the other thread,
we're able to print the value in the fork thread. |-}
forkIOThreads = do
mainThreadId <- myThreadId -- Return information of the thread
input <- newEmptyMVar -- Create an empty MVar
forkIO $ do
inputOtherThread <- takeMVar input
threadId <- myThreadId
putStrLn ("Yes? " ++ " " ++ inputOtherThread ++ " from: " ++ show threadId)
putStrLn ("Knock knock from: " ++ show mainThreadId)
putMVar input "Hello this is Paul!"
threadDelay 1000000
-- | ForkIO operator
-- -----------------------
{-| [forkOS] unlike the ForkIO use the Operation system threads. |-}
forkOSThreads = do
mainThreadId <- myThreadId -- Return information of the thread
input <- newEmptyMVar -- Create an empty MVar
forkOS $ do
inputOtherThread <- takeMVar input
threadId <- myThreadId
putStrLn ("Yes? " ++ " " ++ inputOtherThread ++ " from: " ++ show threadId)
putStrLn ("Knock knock from: " ++ show mainThreadId)
putMVar input "Hello this is Paul!"
threadDelay 1000000
forkIOThreadsCallback :: IO String
forkIOThreadsCallback = do
mainThreadId <- myThreadId -- Return information of the thread
input <- newEmptyMVar -- Create an empty MVar
forkIO $ do
threadDelay 10000000
putMVar input "hello async world"
print "Thread finished:"
output <- takeMVar input
return output
getOperation :: IO String -- an IO monad of type String
getOperation = do
delayTime <- getRandomNumber
threadDelay delayTime
threadId <- myThreadId
print ("Running operation in thread: " ++ show threadId)
return "hello async world!!"
getOperation2 :: IO Int -- an IO monad of type Int
getOperation2 = do
delayTime <- getRandomNumber
threadDelay delayTime
threadId <- myThreadId
print ("Running operation2 in thread: " ++ show threadId)
return 1981
getOperation3 :: IO String
getOperation3 = do
delayTime <- getRandomNumber
threadDelay delayTime
threadId <- myThreadId
print ("Running operation3 in thread: " ++ show threadId)
return "haskell rocks!"
getRandomNumber :: IO Int
getRandomNumber = randomRIO (500000, 1000000 :: Int) -- Random time from 500 to 1000 ms