- Do NOT use loops. Loops destroy composition. Use streaming/ non determinism (multithreaded or not):
forM [1..10] $ \i -> ... use: i <- threads 0 $ choose [1..10]
-- run in the current thread
-- like for i = 1 to 10...
- Do NOT use callbacks. De-invert callbacks with
react
:
onCallback wathever mycallback use: event <- react (onCallback wathever) (return())
myCallbac event= continue event
continue event
- You can Avoid IFs and be more modular (just like haskell parsers do)
if processableByThis
then this
else that use: this' <|> that
where
this' = if not processableByThis then empty else this
- Do NOT fork threads with explicit concurrency use asynchronous primitives and applicatives:
forkIO job1;
forkIO job2; use: (,) <$> async job1 <*> job2
wait for job1 and job2
return (result1,result2)
- Do not fork threads for parallelism. Use alternative:
main= do
forkIO $ doThis
forkIO $ doThat use: main= async doThis <|> async doThat <|> more...
more...
Take care, since async creates a new thread, and every thread continue the execution until the end of the monad or empty
:
do do
forkIO $ doThis >> continue async doThis <|> async doThat
forkIO $ doThat >> continue use: continue
If you don't want them to continue.
func=do do
forkIO $ doThis use: func= (async doThis >> empty) <|> (async doThat >> empty) <|> more
forkIO $ doThat
more
Otherwise, without empty
, func
on the right would return three different results: the one of doThis
, the one of doThat
and the one of more
If you want to reduce parallelism, use thread pooling by adding the threads
modifier, without changing the program code:
processing= do
results <- poolLibrary(numberOfThreads,[dothis,dothat,doOther])
mapM process results -- <- single threaded
use:
processing'= do
eachResult <- threads numberOfThreads $ async doThis <|> async doThat <|> async doOther
process eachResult -- <- still multithreaded
The first return a list of result while the second return different results in different threads to continue the parallelism.
Use collect
if you want to collect the results and generate the list of result in a single thread:
processResults <- collect 0 processing'
0 as parameter forces collect
to wait until there are no more threads active in processing'
- Do not communicate threads (coroutines) with mutable variables using loops (loops are the end of composability) Use streaming/event vars:
someVar = new...
forkIO $ loop1 someVar
forkIO $ loop2 someVar evar <- newEVar
where use: noloop1 evar <|> noloop2 evar
loop1 var= do where
r <- wait var noloop1 evar= do
process r r <- readEVar evar
loop1 process r
... ...
Use transient exceptions to fix things and continue tasks without mixing exception code and application code
do do
myTask `onException`$ \e -> do onException $ \e -> when (tryToFix e) continue
if tryTofix e then myTask use: -- or: if tryToFix r then continue else return ()
else throw e myTask
(do
openThis
openThat `onException` $ \(e :: MyException) -> do closeThat; throwIO e)
`onException` $ \(e :: MyException) -> do closeThis; throwIO e
use:
do
openThis `onException` $ \(e :: MyException) -> closeThis e
openThat `onException` $ \(e :: MyException) -> closeThat e
Exceptions propagate back (bubble up like JavaScript events) by default trough the monad until continue
, which resume execution forward or empty
which stop executing; it is not necessary to re-throw them. The code preserve monadic composition. This makes exceptions more useful.
do
dothis
- Do NOT use the OOP patterns like MVC or the Actor Model. Use functions:
Client:
do do
send node message use: result <- runAt node $ local process
result <- receive node -- and run the program in both client and server
Server:
do
message <- receive
result <- process
send result
Node to node communications uses tcp.
Browser:
ajaxRequest url params \result -> process
use: result <- atServer $ local $ process params
-- compile with ghc and ghcjs
-- and run it both in browser and server
Server:
onRequest
[(url, process )
,(url2,process2)
...
Browser-server communications use websockets.
Axiom widgets are coherent with the transient model:
do
text <- inputString (Just "rewrite this text") `fire` OnChange ! atr "
response <- atServer $ accessDatabase text
rawHtml $ p $ show response
where
accessDatabase text= local $ do
#ifndef #ghcjs_HOST_OS
databaseCode text
#else
empty
#endif
Conditional compilation, as seen above, can be used to compile different local
code in browser and server.
inputString
for example, run empty
in the server.