content: {-# LANGUAGE TemplateHaskell #-}
  module Main where	
  
  -- This file contains some examples of closures, which are the
  -- way to express a function invocation in Cloud Haskell. You
  -- need to use closures to run code on a remote system, which is,
  -- after all, the whole point. See the documentation on the function
  -- 'remotable' for more details. Understanding this example is
  -- essential to effectively using Cloud Haskell.
  
  import Remote
  import Remote.Call (mkClosure)
  
  import Data.List (sort)
  
  -- Step #1: Define the functions that we would
  -- like to call remotely. These are just regular
  -- functions.
             
  sayHi :: String -> ProcessM ()
  sayHi s = say $ "Greetings, " ++ s
  
  sayHiPM :: String -> ProcessM String
  sayHiPM s = say ("Hello, "++s) >> 
                 return (sort s)
  
  sayHiIO :: String -> IO String
  sayHiIO s = putStrLn ("Hello, " ++ s) >>
                  return (reverse s)
  
  sayHiPure :: String -> String
  sayHiPure s = "Hello, " ++ s
  
  simpleSum :: Int -> Int -> Int
  simpleSum a b = a + b + 1
  
  -- You can also manually call a closure using invokeClosure.
  -- This is what functions like spawn and callRemote do
  -- internally.
  runAnother :: Closure Int -> ProcessM String
  runAnother c = do mi <- invokeClosure c
                    case mi of
                      Just i -> return $ concat $ replicate i "Starscream"
                      Nothing -> return "No good"
  
  -- This a partial closure: some parameters are provided by the caller,
  -- and some are provided after the closure is invoked. You have to
  -- write the function in a funny way to make this work automatically,
  -- but otherwise it's pretty straightforward.
  funnyHi :: String -> ProcessM (Int -> ProcessM ())
  funnyHi s = return $ \i -> say ("Hello, " ++ (concat $ replicate i (reverse s)))
  
  -- Step #2: Automagically generate closures
  -- for these functions using remotable. For each
  -- given function n, remotable will create a
  -- closure for that function named n__closure.
  -- You can then use that closure with spawn, remoteCall,
  -- and invokeClosure. See examples below.
  
  remotable ['sayHi, 'sayHiIO,'sayHiPure, 'sayHiPM, 'funnyHi, 'runAnother, 'simpleSum]
  
  initialProcess _ = do
                mynid <- getSelfNode
  
                -- spawn and callRemote (and their variants) run
                -- a function on a given node. We indicate which
                -- node by giving a node ID (to keep it simple
                -- we do everything on one node in this
                -- example), and we indicate which function to run
                -- by providing its closure.
  
                -- A simple spawn. Does not block, and the result
                -- we get back is the PID of the new process.
                p <- spawn mynid (sayHi__closure "Zoltan")
                say $ "Got result " ++ show p
  
                -- callRemote is like a synchronous version of spawn.
                -- It will block until the function ends, and returns
                -- its result.
                v <- callRemote mynid ( sayHiPM__closure "Jaroslav")
                say $ "Got result " ++ v
  
                -- We need a different function to call closures in the
                -- IO monad. Also, instead of using the "something__closure"
                -- syntax, you can call the Template Haskell mkClosure
                -- function, which expands to the same thing.
                w <- callRemoteIO mynid ( $(mkClosure 'sayHiIO) "Noodle")
                say $ "Got result " ++ show w
  
                -- Yet another version of callRemote for nonmonadic functions.
                q <- callRemotePure mynid (sayHiPure__closure "Spatula")
                say $ "Got result " ++ show q
  
                -- We can even give closures to closures. They can in turn run
                -- them indirectly (with spawn or callRemote) or directly
                -- (with invokeClosure).
                x <- callRemote mynid (runAnother__closure (simpleSum__closure 1 1))
                say $ "Got result " ++ x
  
                -- This function takes some parameters after closure invocation.
                -- So the value we get back from invokeClosure is actually
                -- a partially evaluated function.
                mfunnyFun <- invokeClosure (funnyHi__closure "Antwerp")
                case mfunnyFun of
                  Just funnyFun -> funnyFun 3
                  Nothing -> say "No good"
  
                return ()
  
  main = remoteInit (Just "config") [Main.__remoteCallMetaData] initialProcess
  
  