--
```
f x = (x,x)

g x = let f = \ y -> (x,y)
w1 = f "z"
w2 = f True
in (x,f)

{-
-- The next two examples cause errors

g x = (\ f -> let w1 = f "z"
w2 = f True
in (x,f))
(x,f)

h f x = let w1 = f "z"
w2 = f True
in (w1,w2)
-}

h :: (forall a . a -> a) -> x -> (x,Bool)
h f x = let w1 = f x
w2 = f True
in (w1,w2)

{-
ex2 = (4,h)
(line 28): Use of h requires at least 1 argument

ex3 = h ( \ x -> 1) 5
ERROR (line 33): Cannot justify constraints in application
*** Expression    : \x -> 1
*** Type          : b -> b
*** Given context : ()
*** Constraints   : Num b
-}

ex4 = h id 5

--- Data Constructors and rank 2 polymorhism

data Test x = C (forall a . a -> x -> (a,x)) x

ex5 = C (\ a x -> (a,x+1)) 3

ex6 = C (\ a x -> (a,not x)) True

f3 (C h n) w = h "z" w

------ Church numerals

data Nat = Z | S Nat

cataNat zobj sfun Z = zobj
cataNat zobj sfun (S x) =
sfun (cataNat zobj sfun x)

plus x y = cataNat y S x
ex7 = plus (S Z) (S (S Z))

times x = cataNat Z (plus x)
one = S Z
two = S one
three = S two
ex8 = times two three

----------------------------------------
data N = N (forall z . z -> (z -> z) -> z)

cataN zobj sfun (N f) = f zobj sfun

n0 = N(\ z s -> z)
n1 = N(\ z s -> s z)
n2 = N(\ z s -> s(s z))
n3 = N(\ z s -> s(s(s z)))
n4 = N(\ z s -> s(s(s(s z))))

n2Int n = cataN 0 (+1) n
ex9 = n2Int n3

--plus x y = cataNat y S x
succN (N f) = N(\ z s -> s(f z s))
plusN x y = cataN y succN x

ex10 = n2Int (plusN n2 n3)

----------------------------------------------------
-- Rank 2 polymorphism
data L1 a = L1 (forall b . b -> (a -> b -> b) -> b)

-- [1,2,3,4]
ex1 = L1 ( \ n  c -> c 1 (c 2 (c 3 (c 4 n))))

toList (L1 f) = f [] (:)

ex11 = toList ex1

cataList nobj cfun [] = nobj
cataList nobj cfun (x:xs) =
cfun x (cataList nobj cfun)

cataL nobj cfun (L1 f) = f nobj cfun

cons x (L1 f) = L1(\ n c -> c x (f n c))

app x y = cataL y cons x

ex12 = app ex1 ex1
ex13 = toList ex12

build :: (forall b . b -> (a -> b -> b) -> b) -> [a]
build f = f [] (:)

cata nobj cfun [] = nobj
cata nobj cfun (x:xs) = cfun x (cata nobj cfun xs)

upto x =
build(\ n c ->
let h m = if m>x
then n
else c m (h (m+1))
in h 1)

mapX f x =
build(\ n c -> cata n (\ y ys -> c (f y) ys) x)

sumX xs = cata 0 (+) xs

{-
sum(map (+1) (upto 3))
sum(map (+1)
(build(\ n c ->
let h m = if m>3
then n
else c m (h (m+1))
in h 1)
sum(build(\ n c ->
cata n (\ y ys -> c (f y) ys)
(build(\ n c ->
let h m = if m>3
then n
else c m (h (m+1))
in h 1)))
sum(build(\ n c ->
let h m = if m>3
then n
else c (f m) (h (m+1))))
cata 0 (+)
(build(\ n c ->
let h m = if m>3
then n
else c (f m) (h (m+1))))
let h m = if m>3
then 0
else (f m) + (h (m+1))

-}

data List a
= Nil
| Cons a (List a)
| Build (forall b . b -> (a -> b -> b) -> b)

cataZ nobj cfun Nil = nobj
cataZ nobj cfun (Cons y ys) = cfun y (cataZ nobj cfun ys)
cataZ nobj cfun (Build f) = f nobj cfun

uptoZ x =
Build(\ n c ->
let h m = if m>x
then n
else c m (h (m+1))
in h 1)

mapZ f x =
Build(\ n c -> cataZ n (\ y ys -> c (f y) ys) x)

sumZ xs = cataZ 0 (+) xs

ex14 = sumZ(mapZ (+1) (uptoZ 3))
ex15 = sum(map (+1) ([1..3]))

{-
Main> ex14
9
(81 reductions, 177 cells)
Main> ex15
9
(111 reductions, 197 cells)
-}
--```