{-# LANGUAGE EmptyDataDecls            #-}
{-# LANGUAGE GADTs                     #-}
{-# LANGUAGE TypeFamilies              #-}
{-# LANGUAGE TypeOperators             #-}
{-# LANGUAGE DeriveDataTypeable        #-}
{-# LANGUAGE NoMonomorphismRestriction #-}
{-# LANGUAGE StandaloneDeriving        #-}
{-# LANGUAGE TemplateHaskell           #-}
{-# LANGUAGE TypeSynonymInstances      #-}

module Generics.Instant.Zipper.Example.Term where

import Data.Maybe
import Data.Typeable

import Generics.Instant
import Generics.Instant.TH
import Generics.Instant.Zipper

-- | Datatype

data Term
    =  Var'     String
    |  Lambda   String  Term
    |  App      Term    Term
    |  If       Term    Term Term
    deriving (Eq, Show, Typeable)

-- | Representation

{-
instance Representable Term where
    type Rep Term = (Var String)
                :+: (Var String :*: Rec Term)
                :+: (Rec Term :*: Rec Term)
                :+: (Rec Term :*: Rec Term :*: Rec Term)

    from (Var'  s)      =       L (Var s)
    from (Lambda s t)  =    R (L (Var s :*: Rec t))
    from (App t1 t2)   = R (R (L (Rec t1 :*: Rec t2)))
    from (If t1 t2 t3) = R (R (R (Rec t1 :*: Rec t2 :*: Rec t3)))
    
    to (L (Var s)                               ) = Var' s
    to (R (L (Var s :*: Rec t))                 ) = Lambda s t
    to (R (R (L (Rec t1 :*: Rec t2)))           ) = App t1 t2
    to (R (R (R (Rec t1 :*: Rec t2 :*: Rec t3)))) = If t1 t2 t3
-}

$(deriveAll ''Term)

-- | Family

data TermFam a where
    Term   :: TermFam Term
    String :: TermFam String

deriving instance Show (TermFam a)
    
instance Family TermFam

-- | Zipper

instance Zipper Term
    
-- | fac

fac = Lambda "n"
     (If  (App (App (Var' "=") (Var' "n")) (Var' "0"))
         (Var' "1")
         (App  (App (Var' "+") (Var' "n"))
               (App  (Var' "fac")
                     (App (Var' "pred") (Var' "n")))))


fixFac :: ZipperR Term 
fixFac =  return (enter fac)
      >>= down  Term
      >>= down  Term
      >>= right Term
      >>= right Term
      >>= down  Term
      >>= down  Term
      >>= down  String
      >>= return . set "*"
      >>= return . leave