-----------------------------------------------------------------------
-- 	Haskell: The Craft of Functional Programming
-- 	Simon Thompson
-- 	(c) Addison-Wesley, 1999.

-- 	Chapter 18
-----------------------------------------------------------------------

-- Programming with actions
-- ^^^^^^^^^^^^^^^^^^^^^^^^

module Chapter18 where

import Prelude hiding (lookup)

import IO		-- for isEOF (see note below, aslo)

isEOF = hugsIsEOF	-- this should be commented out in later
			-- versions; it is here because Hugs 1.4
			-- doesn't support isEOF

-- The basics of input/output
-- ^^^^^^^^^^^^^^^^^^^^^^^^^^

-- Reading input is done by getLine and getChar: see Prelude for details.

-- 	getLine :: IO String
-- 	getChar :: IO Char

-- Text strings are written using 
-- 	
-- 	putStr :: String -> IO ()
-- 	putStrLn :: String -> IO ()

-- A hello, world program

helloWorld :: IO ()
helloWorld = putStr "Hello, World!"

-- Writing values in general

-- 	print :: Show a => a -> IO ()


-- The do notation: a series of sequencing examples.
-- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

-- Put a string and newline.

-- 	putStrLn :: String -> IO ()
-- 	putStrLn str = do putStr str
-- 	                  putStr "\n"

-- Put four times.

put4times :: String -> IO ()
put4times str 
  = do putStrLn str
       putStrLn str
       putStrLn str
       putStrLn str

-- Put n times

putNtimes :: Int -> String -> IO ()
putNtimes n str
  = if n <= 1 
       then putStrLn str
       else do putStrLn str
               putNtimes (n-1) str

-- Read two lines, then write a message.

read2lines :: IO ()
read2lines 
  = do getLine
       getLine
       putStrLn "Two lines read."

-- Read then write.

getNput :: IO ()
getNput = do line <- getLine
             putStrLn line

-- Read, process then write.

reverse2lines :: IO ()
reverse2lines
  = do line1 <- getLine
       line2 <- getLine
       putStrLn (reverse line2)
       putStrLn (reverse line1)

-- Last example redefined to use a local definition.

reverse2lines' :: IO ()
reverse2lines'
  = do line1 <- getLine
       line2 <- getLine
       let rev1 = reverse line1
       let rev2 = reverse line2
       putStrLn rev2
       putStrLn rev1

-- Reading an Int.

getInt :: IO Int
getInt = do line <- getLine
            return (read line :: Int) 


-- Iteration and recursion
-- ^^^^^^^^^^^^^^^^^^^^^^^

-- A while loop.

while :: IO Bool -> IO () -> IO ()

while test action
  = do res <- test
       if res then do action
                      while test action
              else return ()

-- Copying input to output.

copyInputToOutput :: IO ()
copyInputToOutput
  = while (do res <- isEOF
              return (not res))
          (do line <- getLine
              putStrLn line)

-- An important example: refer to the text to see why it fails to work as
-- required. (The incorrect version is primed.)

goUntilEmpty' :: IO ()
goUntilEmpty'
 = do line <- getLine
      while (return (line /= []))
            (do putStrLn line
                line <- getLine
                return ())

-- The correct program: the key is to think recursively.

goUntilEmpty :: IO ()
goUntilEmpty
  = do line <- getLine
       if (line == [])
          then return () 
          else (do putStrLn line
                   goUntilEmpty)


-- Adding a sequence of integers

sumInts :: IO Int

sumInts
  = do n <- getInt
       if n==0 
          then return 0
          else (do m <- sumInts
                   return (n+m))

-- Addiing a sequence of integers, courteously.

sumInteract :: IO ()
sumInteract
  = do putStrLn "Enter integers one per line"
       putStrLn "These will be summed until zero is entered"
       sum <- sumInts
       putStr "The sum was "
       print sum



-- The calculator
-- ^^^^^^^^^^^^^^

-- This is available separately.


-- Input and output as lazy lists
-- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

-- Reverse all the lines in the input.

listIOprog :: String -> String

listIOprog = unlines . map reverse . lines



-- Monads for Functional Programming
-- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

-- The definition of the Monad class
-- 	class Monad m where
-- 	  (>>=)  :: m a -> (a -> m b) -> m b
-- 	  return :: a -> m a
-- 	  fail   :: String -> m a

-- Kelisli composition for monadic functions.

-- (>@>) :: Monad m => (a -> m b) ->
--                     (b -> m c) ->
--                     (a -> m c)

-- f >@> g = \ x -> (f x) >>= g


-- Some examples of monads
-- ^^^^^^^^^^^^^^^^^^^^^^^

-- Some examples from the standard prelude.

-- The list monad

-- 	instance Monad [] where
-- 	  xs >>= f  = concat (map f xs)
-- 	  return x  = [x]
-- 	  zero      = []

-- The Maybe monad

-- 	instance Monad Maybe where
-- 	  (Just x) >>= k  =  k x
-- 	  Nothing  >>= k  =  Nothing
-- 	  return          =  Just

-- The identity monad

data Id a = Id a 

instance Monad Id where
  return         = Id
  (>>=) (Id x) f = f x

-- The parsing monad

-- 	data SParse a b = SParse (Parse a b)

-- 	instance Monad (SParse a) where
-- 	  return x = SParse (succeed x)
-- 	  zero     = SParse fail
-- 	  (SParse pr) >>= f 
-- 	    = SParse (\s -> concat [ sparse (f x) rest | (x,rest) <- pr st ])

-- 	sparse :: SParse a b -> Parse a b
-- 	sparse (SParse pr) = pr

-- A state monad (the state need not be a table; this example is designed
-- to support the example discussed below.)

type Table a = [a]

data State a b = State (Table a -> (Table a , b))

instance Monad (State a) where

  return x = State (\tab -> (tab,x))

  (State st) >>= f 
    = State (\tab -> let 
                     (newTab,y)    = st tab
                     (State trans) = f y 
                     in
                     trans newTab)


-- Example: Monadic computation over trees
-- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

-- A type of binary trees.

data Tree a = Nil | Node a (Tree a) (Tree a)

-- Summing a tree of integers

-- A direct solution:

sTree :: Tree Int -> Int

sTree Nil            = 0
sTree (Node n t1 t2) = n + sTree t1 + sTree t2

-- A monadic solution: first giving a value of type Id Int ...

sumTree :: Tree Int -> Id Int

sumTree Nil = return 0

sumTree (Node n t1 t2)
  = do num <- return n
       s1  <- sumTree t1
       s2  <- sumTree t2
       return (num + s1 + s2)

-- ... then adapted to give an Int solution

sTree' :: Tree Int -> Int

sTree' = extract . sumTree

-- where the value is extracted from the Id monad thus:

extract :: Id a -> a
extract (Id x) = x


-- Using a state monad in a tree calculation
-- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

-- The top level function ...

numTree :: Eq a => Tree a -> Tree Int

-- ... and the function which does all the work:

numberTree :: Eq a => Tree a -> State a (Tree Int)

-- Its structure mirrors exactly the structure of the earlier program to
-- sum the tree.

numberTree Nil = return Nil

numberTree (Node x t1 t2)
  = do num <- numberNode x
       nt1 <- numberTree t1
       nt2 <- numberTree t2
       return (Node num nt1 nt2)

-- The work of the algorithm is done node by node, hence the function

numberNode :: Eq a => a -> State a Int

numberNode x = State (nNode x)

nNode :: Eq a => a -> (Table a -> (Table a , Int))
nNode x table
  | elem x table        = (table      , lookup x table)
  | otherwise           = (table++[x] , length table)
--  
-- Looking up a value in the table; will side-effect the table if the value
-- is not present.

lookup :: Eq a => a -> Table a -> Int

lookup = lookup 	-- dummy definition: 
			-- exercise for the reader

-- Extracting a value froma state monad.

extractSt :: State a b -> b
extractSt (State st) = snd (st [])

-- The top-level function defined eventually.

numTree = extractSt . numberTree



