{-# LANGUAGE TypeFamilies, DataKinds, TypeOperators, MultiParamTypeClasses , FlexibleInstances, PolyKinds, UndecidableInstances, AllowAmbiguousTypes , RankNTypes, ScopedTypeVariables, FlexibleContexts, ConstraintKinds #-} module Control.Instances.Morph (GenMorph(..), DB, db, Morph, morph) where import Control.Instances.ShortestPath import Control.Monad.Identity import Control.Monad.Trans.Maybe import Control.Monad.Trans.List import Data.Maybe import Data.Proxy -- | States that 'm1' can be represented with 'm2'. -- That is because 'm2' contains more infromation than 'm1'. -- -- The 'MMorph' relation defines a natural transformation from 'm1' to 'm2' -- that keeps the following laws: -- -- > morph (return x) = return x -- > morph (m >>= f) = morph m >>= morph . f -- -- It is a reflexive and transitive relation. -- type Morph m1 m2 = GenMorph DB m1 m2 -- | Lifts the first monad into the second. morph :: Morph m1 m2 => m1 a -> m2 a morph = genMorph db -- instance GenMorph DB m1 m2 => Morph m1 m2 where -- morph = genMorph db -- | A generalized version of 'Morph'. Can work on different -- rulesets, so this should be used if the ruleset is to be extended. type GenMorph db m1 m2 = ( CorrectPath m1 m2 (Path db m1 m2) , GeneratableMorph db (Path db m1 m2) , Morph' (Path db m1 m2) m1 m2 ) type Path db m1 m2 = TransformPath (PathFromList (ShortestPath (ToMorphRepo db) m1 m2)) -- | Lifts the first monad into the second. genMorph :: forall db m1 m2 a . GenMorph db m1 m2 => db -> m1 a -> m2 a genMorph db = repr (generateMorph db :: (Path db m1 m2)) class Morph' fl x y where repr :: fl -> x a -> y a instance Morph' r y z => Morph' (ConnectMorph x y :+: r) x z where repr (ConnectMorph m :+: r) = repr r . m instance (Morph' r m x, Monad m) => Morph' (IdentityMorph m :+: r) Identity x where repr (IdentityMorph :+: r) = (repr r :: forall a . m a -> x a) . return . runIdentity instance Morph' (MUMorph m :+: r) m Proxy where repr (MUMorph :+: _) = const Proxy instance Morph' NoMorph x x where repr _ = id infixr 6 :+: data a :+: r = a :+: r data NoMorph = NoMorph type family ToMorphRepo db where ToMorphRepo (cm :+: r) = TranslateConn cm ': ToMorphRepo r ToMorphRepo NoMorph = '[] type DB = ConnectMorph_2m Maybe MaybeT :+: ConnectMorph_mt MaybeT :+: ConnectMorph Maybe [] :+: ConnectMorph_2m [] ListT :+: ConnectMorph (MaybeT IO) (ListT IO) :+: NoMorph db :: DB db = ConnectMorph_2m (MaybeT . return) :+: ConnectMorph_mt (MaybeT . liftM Just) :+: ConnectMorph (maybeToList) :+: ConnectMorph_2m (ListT . return) :+: ConnectMorph (ListT . liftM maybeToList . runMaybeT) :+: NoMorph -- | This class provides a way to construct the value-level transformations -- from the type-level path and a rulebase. class GeneratableMorph db ch where generateMorph :: db -> ch instance GeneratableMorph db NoMorph where generateMorph _ = NoMorph instance GeneratableMorph db r => GeneratableMorph db ((IdentityMorph m) :+: r) where generateMorph db = IdentityMorph :+: generateMorph db instance GeneratableMorph db r => GeneratableMorph db (MUMorph m :+: r) where generateMorph db = MUMorph :+: generateMorph db instance (HasMorph db (ConnectMorph a b), GeneratableMorph db r) => GeneratableMorph db (ConnectMorph a b :+: r) where generateMorph db = getMorph db :+: generateMorph db -- | This class extracts a given morph from the set of rules class HasMorph r m where getMorph :: r -> m instance {-# OVERLAPPING #-} Monad k => HasMorph (ConnectMorph_2m a b :+: r) (ConnectMorph a (b k)) where getMorph (ConnectMorph_2m f :+: _) = ConnectMorph f instance {-# OVERLAPPING #-} Monad k => HasMorph (ConnectMorph_mt t :+: r) (ConnectMorph k (t k)) where getMorph (ConnectMorph_mt f :+: _) = ConnectMorph f instance {-# OVERLAPS #-} HasMorph r m => HasMorph (c :+: r) m where getMorph (_ :+: r) = getMorph r instance {-# OVERLAPPABLE #-} HasMorph (m :+: r) m where getMorph (c :+: _) = c -- | Checks if the path is found to provide usable error messages class CorrectPath from to path instance CorrectPath from to (a :+: b) instance CorrectPath from to NoMorph data ConnectMorph m1 m2 = ConnectMorph (forall a . m1 a -> m2 a) data ConnectMorph_2m m1 m2 = ConnectMorph_2m (forall a k . Monad k => m1 a -> m2 k a) data ConnectMorph_mt mt = ConnectMorph_mt (forall a k . Monad k => k a -> mt k a) data IdentityMorph (m :: * -> *) = IdentityMorph data MUMorph m = MUMorph -- | Transforms a path element from the generic format to the specific one type family TranslateConn m where TranslateConn (ConnectMorph m1 m2) = Connect m1 m2 TranslateConn (ConnectMorph_2m m1 m2) = Connect_2m m1 m2 TranslateConn (ConnectMorph_mt mt) = Connect_mt mt -- | Transforms the path from the generic format to the specific one type family TransformPath m where TransformPath (Connect m1 m2 :+: r) = ConnectMorph m1 m2 :+: TransformPath r TransformPath (Connect_id m :+: r) = IdentityMorph m :+: TransformPath r TransformPath (Connect_MU m :+: r) = MUMorph m :+: TransformPath r TransformPath NoMorph = NoMorph TransformPath NoPathFound = NoPathFound type family PathFromList ls where PathFromList '[] = NoMorph PathFromList '[ NoPathFound ] = NoPathFound PathFromList (c ': ls) = c :+: PathFromList ls