--
-- Examples for use with LambdaVr
--

-- Simple functional version: -------------------------------------------------

data Tree a = Leaf a | Tree a :^: Tree a

label     :: Tree a -> Tree (a,Int)
label tree = fst (lab tree 0)
 where lab (Leaf n)  c  =  (Leaf (n,c), c+1)
       lab (l :^: r) c  =  (l' :^: r', c'')
                           where (l',c')  = lab l c
                                 (r',c'') = lab r c'

-- Lambda var version: --------------------------------------------------------

counter = var (\cnt -> 0 =: cnt >>
                       result (cnt        ? \c ->
                               c+1 =: cnt >>
                               result c))

label0 tree = pure (counter >>= lab tree)

lab (Leaf n)  ctr = ctr                   >>= \c ->
                    result (Leaf (n,c))
lab (l :^: r) ctr = lab l ctr             >>= \l' ->
                    lab r ctr             >>= \r' ->
                    result (l' :^: r')

{- Here is an example where pure is not safe:

label0 tree = pure (lab tree)
 where ctr           = pure counter
       lab (Leaf n)  = ctr                   >>= \c ->
                       result (Leaf (n,c))
       lab (l :^: r) = lab l                 >>= \l' ->
                       lab r                 >>= \r' ->
                       result (l' :^: r')

 gives    label0 aTree = (Leaf (1,0) :^: Leaf (2,1)) :^:
                         (Leaf (3,2) :^: Leaf (4,3))

whereas:

label0 tree = pure (lab tree)
 where lab (Leaf n)  = pure counter          >>= \c ->
                       result (Leaf (n,c))
       lab (l :^: r) = lab l                 >>= \l' ->
                       lab r                 >>= \r' ->
                       result (l' :^: r')

 gives    label0 aTree = (Leaf (1,0) :^: Leaf (2,0)) :^:
                         (Leaf (3,0) :^: Leaf (4,0))
-}

-- State monad version: -------------------------------------------------------

data State s a = ST (s -> (a,s))

instance Functor (State s) where
    map f (ST st) = ST (\s -> let (x,s') = st s in (f x, s'))

instance Monad (State s) where
    result x      = ST (\s -> (x,s))
    ST m `bind` f = ST (\s -> let (x,s') = m s
                                  ST f'  = f x
                              in  f' s')

startingWith         :: State s a -> s -> a
ST m `startingWith` v = fst (m v)

incr :: State Int Int
incr  = ST (\s -> (s,s+1))

label1     :: Tree a -> Tree (a,Int)
label1 tree = lab tree `startingWith` 0
 where lab (Leaf n)  = incr                  `bind` \c ->
                       result (Leaf (n,c))
       lab (l :^: r) = lab l                 `bind` \l' ->
                       lab r                 `bind` \r' ->
                       result (l' :^: r')

label2     :: Tree a -> Tree (a,Int)
label2 tree = lab tree `startingWith` 0
 where lab (Leaf n)  = [ Leaf (n,c) | c <- incr ]
       lab (l :^: r) = [  l :^: r   | l <- lab l, r <- lab r ]


-- sample data: ---------------------------------------------------------------

aTree = balance [1..4]

balance ns | len == 1   =  Leaf (head ns)
           | otherwise  =  balance (take h ns) :^: balance (drop h ns)
             where len = length ns
                   h   = len `div` 2

balance' ns = bal (length ns) ns
 where bal l ns | l == 1    = Leaf (head ns)
                | otherwise = let h = l `div` 2
                              in  bal h (take h ns) :^: bal (l-h) (drop h ns)

-------------------------------------------------------------------------------
-- A swap function:

swap    :: Var a -> Var a -> Proc ()
swap v w = v ? \x ->
           w ? \y ->
           x =: w >>
           y =: v

valOf v = v ? result 

-- usage: swap elements of arrays a and b in the range between 1 and n
--
--seq [swap (a!i) (b!i) | i <- [1..n]]


increment v = v ? \val -> val+1 =: v

anotherTest = var (\v -> 0 =: v      >>
                         increment v >>
                         increment v >>
                         increment v >>
                         increment v >>
                         v           ?
                         result)

swapTest = var (\v ->
           var (\w ->
           "I'm v" =: v >>
           "I'm w" =: w >>
           swap v w     >>
           v            ? \vValue ->
           w            ? \wValue ->
           result (vValue,wValue)))


swapTest2 = var           (\v ->
            var           (\w -> 
            0  =: v       >>
            10 =: w       >>
            v             ? \vValue ->
            vValue+1 =: v >>
            swap v w      >>
            v             ? \vValue ->
            w             ? \wValue ->
            result (vValue,wValue)))
            
-- A queue implementation

-- First, its interface:

type Queue a = ( a -> Proc (),  -- put
                 Proc a,        -- get
                 Proc Bool      -- isempty
               )

-- Procedures to take apart the method tuple:

put (p, g, i) = p
get (p, g, i) = g
isempty (p, g, i) = i

-- Now, the implementation in terms of a linked list:

data Link a = Link a (Var (Link a))

mkqueue :: Proc (Queue Int)
mkqueue = 
  var (\v ->
  var (\front -> v =: front >>
  var (\rear  -> v =: rear  >>
   result
    ( \x ->                             -- put x
      rear ? \r -> 
      var (\r' ->
      Link x r' =: r >>
      r' =: rear)
    ,
      front ? \f ->                     -- get
      f ? \ (Link x f') ->
      f' =: front >>
      result x
    ,
      front ? \f ->                     -- isempty
      rear ? \r ->
      result (f == r)
    )
   )))

-- Usage:

qTest = pure (mkqueue        >>= \q     ->
              put q 1        >>
              get q          >>= \first ->
              isempty q      >>= \empty ->
              result (if first == 1 && empty then "so should it be"
                                             else "something's wrong"))

-- An alternative way to write the same thing:

mkqueue1 :: Proc (Queue Int)
mkqueue1  = 
  newvar     >>= \v     ->
  newvar     >>= \front ->
  v =: front >>
  newvar     >>= \rear  ->
  v =: rear  >>
  let
      put x   = rear?             \r ->
                newvar         >>= \r' ->
                Link x r' =: r >>
                r' =: rear

      get     = front?            \f ->
                f?                \(Link x f') ->
                f' =: front    >>
                result x

      isempty = front          ?  \f ->
                rear           ?  \r ->
                result (f==r)
  in
      result (put, get, isempty)

-- Usage:

qTest1 = pure (mkqueue1       >>= \q     ->
               put q 1        >>
               get q          >>= \first ->
               isempty q      >>= \empty ->
               result (if first == 1 && empty then "so should it be"
                                              else "something's wrong"))

qTest2 = mkqueue1       >>= \q     ->
         put q 1        >>
         get q          >>= \first ->
         isempty q      >>= \empty ->
         result (if first == 1 && empty then "so should it be"
                                        else "something's wrong")

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