packages feed

remote-0.1: examples/tests/Test-Closure.hs

{-# 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