-- 
--
--

data Abn = EmptyAbn | LeafAbn Int | NodeAbn Int Abn Abn
    deriving(Show)

data Abf = EmptyAbf | LeafAbf (Int -> Int) | NodeAbf (Int -> Int) Abf Abf


anAbn1 = NodeAbn 5 (LeafAbn 4) (NodeAbn 3 EmptyAbn (LeafAbn 6))
anAbn2 = NodeAbn 5 (LeafAbn 4) (NodeAbn 3 EmptyAbn EmptyAbn)

inc x = x+1
double x = x+x
square x = x*x


anAbf = NodeAbf inc (LeafAbf double) (NodeAbf square EmptyAbf (LeafAbf inc))

appT :: Abf -> Abn -> Abn

appT EmptyAbf EmptyAbn = EmptyAbn

appT (LeafAbf f) (LeafAbn n) = LeafAbn (f n)

appT (NodeAbf f abf1 abf2) (NodeAbn n abn1 abn2) =
                        (NodeAbn (f n) (appT abf1 abn1) (appT abf2 abn2))

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



-- senza uso di >>= 

pappT :: Abf ->  Abn -> (Maybe Abn)

pappT EmptyAbf EmptyAbn = (Just EmptyAbn)

pappT (LeafAbf f) (LeafAbn n) = (Just (LeafAbn (f n)))

pappT (NodeAbf f abf1 abf2) (NodeAbn n abn1 abn2) =
               case (pappT abf1 abn1) of
                    Nothing -> Nothing
                    Just t1 -> case (pappT abf2 abn2) of
                                     Nothing -> Nothing
                                     Just t2  -> (Just (NodeAbn (f n) t1 t2))
pappT _ _ = Nothing

partialAppT :: 

----------------------
--
-- con uso di >>=

-- prima di vedere la soluzione che usa >>=
-- osserviamo che Abn e Abf in realta' potrebbero essere istanze
-- del seguente tipo parametrico

data AbL a = EmptyAbL | LeafAbL a | NodeAbL a (AbL a) (AbL a)
    deriving(Show)

type Abn = AbL Integer
type Abf = AbL (Integer -> Integer)

-- ovviamente gli alberi che avevamo definito per usarli
-- come test vanno ridefiniti

anAbn3 = (NodeAbL 5 (LeafAbL 4) (NodeAbL 3 EmptyAbL (LeafAbL 6)))
anAbn4 = (NodeAbL 5 (LeafAbL 4) (NodeAbL 3 EmptyAbL EmptyAbL))


anAbf2 = NodeAbL inc (LeafAbL double) (NodeAbL square EmptyAbL (LeafAbL inc))

-- definiamo ora la nostra funzione

pappT1 :: Abf ->  Abn -> (Maybe Abn)


pappT1 EmptyAbL EmptyAbL = (Just EmptyAbL)

pappT1 (LeafAbL f) (LeafAbL n) = (Just (LeafAbL (f n)))

pappT1 (NodeAbL f abf1 abf2) (NodeAbL n abn1 abn2) =
                         pappT1 abf1 abn1 >>=
                \t1 -> (pappT1 abf2 abn2) >>=
                \t2 -> Just (NodeAbL (f n) t1 t2)

pappT1 _ _ = Nothing


-- notiamo come sia possibile utilizzare la notazione "do"
-- per ottenere un codice piu' leggibile


pappT :: Abf ->  Abn -> (Maybe Abn)


pappT EmptyAbL EmptyAbL = (Just EmptyAbL)

pappT (LeafAbL f) (LeafAbL n) = (Just (LeafAbL (f n)))

pappT (NodeAbL f abf1 abf2) (NodeAbL n abn1 abn2) =
         do t1 <- pappT abf1 abn1
            t2 <- pappT abf2 abn2
            Just (NodeAbL (f n) t1 t2)

pappT _ _ = Nothing





