module MonadenBeispiele2
{-
Die Beispiele aus der Vorlesung von Montag 16. Januar.
-}
where
import Control.Monad

{-
higher-order Kombinatoren für Monaden
entsprechende Funktionen sind schon in Control.Monad vordefiniert

map :: (a -> b) -> [a] -> [b]
filter (a -> Bool) -> [a] -> [a]
-}

myMapM :: Monad m => (a -> m b) -> [a] -> m [b]
myMapM _ [] = return []
myMapM f (h:t) = do
  hh <- f h
  tt <- myMapM f t
  return $ hh:tt

myFilterM :: Monad m => (a -> m Bool) -> [a] -> m [a]
myFilterM _pred [] = return []
myFilterM pred (h:t) = do
  c <- pred h
  rest <- myFilterM pred t
  if c then return $ h:rest
       else return rest

{-
import System.Posix.Files
bsp4 :: IO [String]
bsp4 = myFilterM System.Posix.Files.fileExist ["/","/root","/home","/foobar"]
-}

{- original Definition in : Control.Monad
filterM          :: (Monad m) => (a -> m Bool) -> [a] -> m [a]
filterM _ []     =  return []
filterM p (x:xs) =  do
   flg <- p x
   ys  <- filterM p xs
   return (if flg then x:ys else ys)
   Frage: warum ist das Orginal besser ? Was ist genau der Unterschied ?
-}


{-
ndet :: Int -> [Int]
ndet n = do
          (return $ n+3)
  `mplus` (return $ n + 2)
-}

ndet :: Int -> [Int]
ndet n = [ n+3 , n +2]

-- myList l1 l2 = [(x,y) | x <-l1, y <- l2]
myList l1 l2 = do
  x <-l1
  y <- l2
  return (x,y)

-- seq l = filterM (\x-> [True,False]) l
sequences :: MonadPlus m => [a] -> m [a] 
sequences
  = filterM (const ( return False `mplus` return True))

bsp1 :: [[Int]]
bsp1 = myMapM ndet [1,2,3]

{-
vordefinierte Instanzen für [] in GHC.Base :
instance  Monad []  where
    m >>= k             = foldr ((++) . k) [] m
    m >> k              = foldr ((++) . (\ _ -> k)) [] m
    return x            = [x]
    fail _              = []

-- vereinfacht :
instance  Monad []  where
    (>>=) = concatMap
    return x            = [x]


instance MonadPlus [] where
   mzero = []
   mplus = (++)
-}

{--
instanzen sind normalerweise fuer einen Datentyp definiert
(newtype oder data)

newtype definiert einen neuen Datentype
ein newtype Datentyp ist ein Wrapper fuer einen anderen Datentype
Im Beispiel: wrappen mit NDet ,  unWrappen mit unDet

--}

newtype NDet a
  = NDet {unDet :: [a]}
  deriving Show


{-- Handgeschriebene Instanzen für NDet --}
instance Monad NDet where
  (>>=) (NDet l) f = NDet $ concatMap (unDet . f) l
  return x = NDet [x]

instance MonadPlus NDet where
  mzero = NDet []
  mplus (NDet l1) (NDet l2) = NDet (l1 ++ l2) 

{--
mit der Erweiterung { -# LANGUAGE GeneralizedNewtypeDeriving #- }
kann der Compiler Instanzen fuer newtype selber generieren

newtype NDet a
  = NDet {unDet :: [a]}
  deriving (Show,Monad,MonadPlus) 
Das geht aber nur, weil der [] schon instanz von Monad und MonadPlus ist.


--}

bsp2 :: NDet [Int]
bsp2 = sequences [1,2,3]

bsp3 :: NDet [Int]
bsp3 = forM [1,2,3] $ \x -> return x `mplus` return (negate x)

