Skip to content

Instantly share code, notes, and snippets.

@agocorona
Last active September 28, 2022 13:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save agocorona/a6cc533731de5b7bb6066d59fdcac8cb to your computer and use it in GitHub Desktop.
Save agocorona/a6cc533731de5b7bb6066d59fdcac8cb to your computer and use it in GitHub Desktop.
  • 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment