data List a = Nil | Cons a (List a) deriving Show

foldList :: (a -> b -> b) -> b -> List a -> b
foldList _ g Nil = g
foldList f g (Cons x xs) = f x (foldList f g xs)

mapList :: (a -> b) -> List a -> List b
mapList f = foldList (\x xs -> Cons (f x) xs) Nil

-- infinite list
from :: Integer -> [Integer]
from n = n : from (n+1)
nats = from 0

myTake :: Int -> [a] -> [a]
myTake 0 _ = []
myTake _ [] = []
myTake (n+1) (x:xs) = x : myTake n xs

dropMult :: Integer -> [Integer] -> [Integer]
dropMult x xs = x : filter (\y -> y `mod` x /= 0) xs

dropAll :: [Integer] -> [Integer]
dropAll (x:xs) = dropMult x (dropAll xs) 

primes :: [Integer]
primes = dropAll (from 2)

-- infinite data objects
ones = 1 : ones

nats2 = 0 : map (\x -> x + 1) nats2

-- Hamming list
mer :: [Integer] -> [Integer] -> [Integer]
mer (x:xs) (y:ys) | x <  y = x : mer xs (y:ys)
                  | x == y = x : mer xs ys
                  | True   = y : mer (x:xs) ys

mer3 :: [Integer] -> [Integer] -> [Integer] -> [Integer]
mer3 xs ys zs = mer (mer xs ys) zs

ham = 1 : mer3 (map (2*) ham) (map (3*) ham) (map (7*) ham)

-- INPUT / OUTPUT

putString :: String -> IO ()
putString [] = return ()
putString (x:xs) = putChar x >> putString xs

getS :: IO String
getS = getChar >>= \x -> if x == '\n' then return [] else (getS >>= \xs -> return (x:xs))

gets :: IO String
gets = do
  x <- getChar
  if x == '\n' then return [] else do
    xs <- gets
    return (x:xs)

gets2 :: IO String
gets2 = getChar >>= \x -> (if x == '\n' then return [] else gets2 >>= \xs -> return (x:xs))

main :: IO ()
main = do
  putString "Please enter your name:"
  name <- gets
  putString ("Hello " ++ name ++ "!")
