-- | This module is my answer to the pattern discussed in
-- http://blog.ezyang.com/2011/06/a-pattern-for-increasing-sharing/ about maximizing sharing when
-- transforming an algebraic data type.
--
-- The' 'Transhare' class is a kind of degerate case of 'Traverse' building on a new 'Applicative'
-- data type called 'TransResult' defined below.  The result 'transM' is a way to lift a
-- parsimonious transformer 'a -> Maybe a', which indicates identity with 'Nothing', to work on a
-- container with maximized sharing.
module Data.Transhare where

import Control.Applicative(Applicative(..),(<$>))
import Data.Tree(Tree(..))

data TransResult a = Original    { getTrans :: a } 
                   | Transformed { getTrans :: a }
  deriving (Show,Eq)

instance Functor TransResult where
  fmap f (Original a) = Original (f a)         -- needed for default implementation of (*>) to pass ignore_left_value_law
  fmap f (Transformed a) = Transformed (f a)

instance Applicative TransResult where
  pure a = Original a
  (Original x) <*> (Original y) = Original (x y)
  tx <*> ty = Transformed (getTrans tx (getTrans ty))

-- | 'TransM' is a parsimonious transformer that can return 'Nothing' when the transformation is an identity.
--
-- If the result is 'Just' 't' then the result 't' might or might not be identical to the argument.
type TransM a = a -> Maybe a

-- | 'TransR' is a parsimonious transformer that returns (Original x) only if x is the original argument.
-- 
-- This must follow the law that TransMR . TransRM . t = t
--
-- The disadvantage of 'TransR' compared to 'TransM' is ensuring the above law and that sharing for
-- Original results is actually being done.
--
-- 'TransR' which implement sharing correctly are "proper implementations" of 'TransR'
type TransR a = a -> TransResult a


-- | 'transMR' creates a proper implementation of 'transR' from any 'transM'
transMR :: TransM a -> TransR a
transMR t = \a -> maybe (Original a) Transformed (t a)

-- | 'transRM' creates a proper implementation of 'transM' only from a proper implementation of 'transR'
transRM :: TransR a -> TransM a
transRM t = \a -> case t a of Original _ -> Nothing
                              Transformed a -> Just a

-- | 'fromO' is a helper function used with Applicative to ensure the 'TransR' computed by 'transR'
-- are proper implementations.
fromO :: a -> TransResult a -> TransResult a
fromO a (Original _) = Original a
fromO _ b = b

class Transhare f where
  transM :: TransM a -> TransM (f a)
  transM  = transRM . transR . transMR
  transR :: TransR a -> TransR (f a)

instance Transhare ((,) a) where
  transR t = \ x@(a,b) -> fromO x $ (,) a <$> t b

instance Transhare (Either a) where
  transR t = let tE x@(Right b) = fromO x $ Right <$> t b
                 tE x = Original x
             in tE

instance Transhare [] where
  transR t = let tL x@((:) v vs) = fromO x $ (:) <$> t v <*> transR t vs
                 tL x = Original x
             in tL

instance Transhare Tree where
  transR t = \ a@(Node value children) -> fromO a $ Node <$> t value <*> transR (transR t) children

instance Transhare TransResult where
  transR t = let tR x@(Original a) = fromO x $ Original <$> t a
                 tR x@(Transformed a) = fromO x $ Transformed <$> t a
             in tR

-- 'transResult_laws' checks that the Functor and Applicative instances do what they are supposed to
-- do.
transResult_laws :: Bool
transResult_laws = all and $
    [ -- Check the tainting of Transformed 
      [ (Transformed (error "discarded") *> Original 'q') == Transformed 'q'
      , (Original 'q' <* Transformed (error "discarded")) == Transformed 'q'
      , (Original (error "discarded") *> Transformed 'q') == Transformed 'q'
      , (Transformed 'q' <* Original (error "discarded")) == Transformed 'q'
      ]
    , let fmap_law f x = fmap f x == (pure f <*> x)
      in [ fmap_law f x | f <- [succ], x <- [o,t] ]
    , let identity_law v = (pure id <*> v) == v
      in [ identity_law v | v <- [o,t]]
    , let composition_law u v w = (pure (.) <*> u <*> v <*> w) == (u <*> (v <*> w))
          uo = Original succ
          ut = Transformed succ
          vo = Original (pred.pred)
          vt = Transformed (pred.pred)
      in [ composition_law u v w | u <- [uo,ut], v <- [vo,vt], w <- [o,t]] 
    , let homomorphism_law f x = (pure f <*> pure x) == (pure (f x) :: TransResult Char)
      in [ homomorphism_law f x | f <- [succ], x <- ['x'] ]
    , let interchange_law u y = (u <*> pure y) == (pure ($ y) <*> u)
          uo = Original succ
          ut = Transformed succ
      in [ interchange_law u y | u <- [uo,ut], y <- ['y']]
    , let ignore_left_value_law u v = (u *> v) == (pure (const id) <*> u <*> v)
          uo = Original (error "discard uo")
          ut = Transformed (error "discard ut")
      in [ ignore_left_value_law u v| u <- [uo,ut], v <- [o,t]]
    , let ignore_right_value_law u v = (u <* v) == (pure const <*> u <*> v)
          vo = Original (error "discard vo")
          vt = Transformed (error "discard vt")
      in [ ignore_right_value_law u v| u <- [o,t], v <- [vo,vt]]
    ]
 where
   o = Original 'o'
   t = Transformed 't'