{-# LANGUAGE TemplateHaskell, DeriveDataTypeable #-}
-----------------------------------------------------------------------------
-- |
-- Module      :  ForSyDe.AbsentExt
-- Copyright   :  (c) ES Group, KTH/ICT/ES 2007-2013
-- License     :  BSD-style (see the file LICENSE)
--
-- Maintainer  :  forsyde-dev@ict.kth.se
-- Stability   :  experimental
-- Portability :  portable
--
-- The 'AbstExt' is used to extend existing data types with the value
--  \'absent\', which models the absence of a value.
--
-----------------------------------------------------------------------------

module ForSyDe.Deep.AbsentExt(
                  AbstExt (Abst, Prst), fromAbstExt, unsafeFromAbstExt,
                  abstExt, psi,
                  isAbsent, isPresent, abstExtFunc)
                where

import Data.Data
import Language.Haskell.TH.Lift


-- |The data type 'AbstExt' has two constructors. The constructor 'Abst' is used to model the absence of a value, while the constructor 'Prst' is used to model present values.
data AbstExt a                 =  Abst
                               |  Prst a
  deriving (Eq, Data, Typeable)
$(deriveLift1 ''AbstExt)



-- |The function 'fromAbstExt' extracts the inner value contained in 'AbstExt'
fromAbstExt            :: a -- ^ Default value returned if the input is 'Abst'
                       -> AbstExt a -> a
-- |Similar to 'fromAbstExt', but without default value
unsafeFromAbstExt      :: AbstExt a -> a
-- |The functions 'isPresent' checks for the presence of a value.
isPresent              :: AbstExt a -> Bool
-- |The functions 'isAbsent' checks for the absence of a value.
isAbsent               :: AbstExt a -> Bool
-- |The function 'abstExtFunc' extends a function in order to process absent extended values. If the input is (\"bottom\"), the output will also be  (\"bottom\").
abstExtFunc            :: (a -> b) -> AbstExt a -> AbstExt b
-- | The function 'psi' is identical to 'abstExtFunc' and should be used in future.
psi :: (a -> b) -> AbstExt a -> AbstExt b
-- | The function 'abstExt' converts a usual value to a present value.
abstExt :: a -> AbstExt a






-- Implementation of Library Functions

-- | The data type 'AbstExt' is defined as an instance of 'Show' and 'Read'. \'_\' represents the value 'Abst' while a present value is represented with its value, e.g.  'Prst' 1 is represented as \'1\'.
instance Show a => Show (AbstExt a) where
         showsPrec _ x         = showsAbstExt x

showsAbstExt :: Show a => AbstExt a -> [Char] -> [Char]
showsAbstExt Abst              = (++) "_"
showsAbstExt (Prst x)          = (++) (show x)

instance Read a => Read (AbstExt a) where
         readsPrec _ x         =  readsAbstExt x

readsAbstExt                   :: (Read a) => ReadS (AbstExt a)
readsAbstExt s                 =     [(Abst, r1)    | ("_", r1) <- lex s]
                                  ++ [(Prst x, r2)  | (x, r2) <- reads s]

abstExt v                      =  Prst v

fromAbstExt x Abst             =  x
fromAbstExt _ (Prst y)         =  y

unsafeFromAbstExt (Prst x)     =  x
unsafeFromAbstExt Abst         =  error "AbsentExt.unsafeFromAbstExt: Abst"

isPresent Abst                 =  False
isPresent (Prst _)             =  True

isAbsent                       =  not . isPresent

abstExtFunc f                  = f'
  where f' Abst                = Abst
        f' (Prst x)            = Prst (f x)


psi                            = abstExtFunc