packages feed

orc-1.1.1.0: src/Examples/test.hs

--------------------------------------------------------------------------------
-- |
-- Module      : test
-- Copyright   : (c) 2008-2010 Galois, Inc.
-- License     : BSD3
--
-- Maintainer  : John Launchbury <john@galois.com>
-- Stability   :
-- Portability : concurrency
--
-- A test file for the Orc EDSL
--

{-# LANGUAGE GeneralizedNewtypeDeriving #-}

module Main (

    module Orc
  , main

  ) where

import Orc

import Control.DeepSeq
import System.Random(randomIO)
import Data.List(sortBy)

main = do
    putStrLn "Enter a number 1-9 to run that example:"
    ans <- getLine
    putStrLn "----------------------------"
    case ans of
      "1" -> printOrc baseball 
      "2" -> printOrc (collect patterns)
      "3" -> printOrc firstmetro
      "4" -> printOrc parvalueVal
      "5" -> printOrc $ count queens
      "6" -> printOrc numbers 
      "7" -> printOrc (scan (flip (++)) [] (takeOrc 10 queens)) 
      "8" -> printOrc $ count (chans [1 .. 12])
      "9" -> printOrc $ takeOrc 100 $ zipOrc (recursive 1) (recursive 1000)
      _   -> putStrLn "Unknown"
    putStrLn "----------------------------"



-------------------------------------
-- Shows a rich and nested set of Orc interactions

baseball :: Orc (String,String)
baseball = do

  team <-  prompt "Name a baseball team" 
               `notBefore` 10
        <|> prompt "Name another team"
               `butAfter` (12, return "Yankees")
        <|> (delay 8 >> return "Mariners")
           
  agree <- prompt ("Do you like "++team++"?")
             `butAfter` (20, guard (team/="Mets") 
                          >> return "maybe")
                
  return (team, agree)


-----------------------------
-- Pattern guards

patterns :: Orc Int
patterns = do
   x <- liftList [1..10]
   y <- liftList [2,4..10]
   (n,True) <- return (x, x `mod` y /= 0)
   return n

------------------
-- Metronome example. Shows recursion. Also demonstrates the killing
-- of the metronome thread when the output is killed with ^C, or
-- after 3 prompts are fulfilled

firstmetro :: Orc String
firstmetro =  return "Respond to 2 prompt within 15 seconds, then check all prompts are dead"
          <|> (delay 1 >> takeOrc 2 (metronome >> prompt "Hello"))
          <|> (delay 15 >> return "15 seconds...")

metronome = signal <|> (delay 2 >> metronome)

------------------
-- Demonstrates the concurency of 'eagerly'

parvalue :: Orc Quote
parvalue = quotes
     (Query "Quote A: Enter a number")
     (Query "Quote B: Enter a number")

newtype Query = Query {text::String} deriving (Eq,Show)
newtype Quote = Quote {price::Int} deriving (Eq,Show,Num,NFData)
noQuote = Quote 0

quotes :: Query -> Query -> Orc Quote
quotes srcA srcB = do
  quoteA <- eagerly $ getQuote srcA
  quoteB <- eagerly $ getQuote srcB
  cut (  (pure least <*> quoteA <*> quoteB)
     <|> (quoteA >>= threshold)
     <|> (quoteB >>= threshold)
     <|> (delay 25 >> quoteA <|> quoteB) 
     <|> (delay 30 >> return noQuote))

least qa qb = if price qa < price qb then qa else qb
threshold q = guard (price q < 300) >> return q

getQuote q = prompt (text q) >>= (return . Quote . read)

parallelOr orc1 orc2 = do
  ox <- eagerly orc1
  oy <- eagerly orc2
  cut (  (ox >>= guard >> return True)
     <|> (oy >>= guard >> return True) 
     <|> (pure (||) <*> ox <*> oy))


parvalueVal :: Orc Quote
parvalueVal = quotesVal
     (Query "Quote A: Enter a number")
     (Query "Quote B: Enter a number")

quotesVal :: Query -> Query -> Orc Quote
quotesVal srcA srcB = do
  quoteA <- val $ getQuote srcA
  quoteB <- val $ getQuote srcB
  cut (  publish (least quoteA quoteB)
     <|> (threshold quoteA)
     <|> (threshold quoteB)
     <|> (delay 25 >> publish quoteA <|> publish quoteB) 
     <|> (delay 30 >> return noQuote))

parallelOrVal orc1 orc2 = do
  x <- val orc1
  y <- val orc2
  cut ( (guard x >> publish True)
    <|> (guard y >> publish True) 
    <|> publish (x || y)   )



------------------
-- N queens

queens = return ("Computing "++show size++"-queens...")
       <+> fmap show (extend [])


size = 13 :: Int

extend :: [Int] -> Orc [Int]
extend xs
  | length xs == 3 = liftList $ extendL xs
  | otherwise      = do
      j <- liftList [1 .. size]
      guard $ not (conflict xs j)
      extend (j:xs)

conflict :: [Int] -> Int -> Bool
conflict rs n
  =  n `elem` rs                          -- column clash
  || n `elem` zipWith (+) rs [1 .. size]  -- diagonal clash
  || n `elem` zipWith (-) rs [1 .. size]  -- other diagonal

extendL :: (MonadPlus liftList) => [Int] -> liftList [Int]
extendL xs
  | length xs == size = return xs
  | otherwise         = do
      j <- liftList [1 .. size]
      guard $ not (conflict xs j)
      extendL (j:xs)

------------------------
-- Shows numbers interpreted as Orc terms

numbers :: Orc String
numbers = tally $ map prompt ["one","two","three","four"]

tally ps = do n <- fmap sum (syncList (map success ps))
              guard  (n/=2)
              return $ show n
           <?>
              return ("You responded to two, you lose... :-)")

success :: Orc String -> Orc Int
success m = (m >> return 1) `butAfter` (10, return 0)


-----------------------
-- Demonstrates lifting functions to complex Orc expressions. 

pairs :: Orc (String, String)
pairs = sync (,) numbers queens

-----------------------
-- Demonstrate that recursion needs to be guarded, otherwise runs away.

recursive :: Int -> Orc Int
recursive n = do
  putStrLine ("Recursion: "++show n)
  liftList [1,-1] <|> (recursive (n+1) >>= \x->return (x+sign(x)))

sign x = if x<0 then -1 else 1

powerset :: [a] -> Orc [a]
powerset xs = liftList $ filterM (const $ liftList [False, True]) xs

threeset :: [a] -> Orc [a]
threeset xs = do
  ys <- powerset xs
  guard (length ys == 3)
  return ys

hassle = (metronome >> putStrLine("Simon"))
            `onlyUntil` (delay 15 >> signal)

chans :: [a] -> Orc [a]
chans xs = do
     ch <- newChan
     readChans 4000 ch <|>
       silent (count (powerset xs) >>= writeChan ch)

readChans 0 ch = do
  ch' <- dupChan ch
  readChans' ch <|> readChans' ch'
readChans n ch = do
  mxs <- readChan ch
  case mxs of
    Left xs -> readChans (n-1) ch
    Right n -> stop

readChans' ch = do
  mxs <- readChan ch
  case mxs of
    Left xs -> return xs <|> readChans' ch
    Right n -> stop

------------------------------

shuffle :: [a] -> IO [a]
shuffle xs = do
    ks <- sequence $ take (length xs) $ repeat (randomIO :: IO Int)
    return $ map snd $ sortBy key $ zip ks xs
  where
    key (a,x) (b,y) = if a<b then LT else GT