{-# LANGUAGE TemplateHaskell, MultiParamTypeClasses, FlexibleInstances, FlexibleContexts, NoMonomorphismRestriction #-} module Bio.PDB.InstantiateIterable(Iterable(..) , gen_iterable , self_iterable , trans_iterable) where import qualified Bio.PDB.Structure.List as L import Language.Haskell.TH.Syntax import Control.Monad.Identity(runIdentity,foldM) import Data.List(foldl') class Iterable a b where imapM :: (Monad m) => (b -> m b) -> a -> m a imap :: (b -> b) -> a -> a imap f e = runIdentity $ imapM (\b -> return $ f b) e ifoldM :: (Monad m) => (c -> b -> m c) -> c -> a -> m c ifoldr :: (b -> c -> c) -> c -> a -> c ifoldl :: (c -> b -> c) -> c -> a -> c ifoldl' :: (c -> b -> c) -> c -> a -> c ilength :: b -> a -> Int -- NOTE: b is 'dummy' type argument to satisfy Iterable a b constraint -- | Generates a direct instance of iterable between $typA and $typB with -- given names of getter and setter, so that: -- $getter :: $typA -> $typB -- $setter :: $typB -> $typA -> $typA gen_iterable typA typB getter setter = [d| instance Iterable $(typA) $(typB) where imapM f a = do b' <- L.mapM f ( $(getter) a) return $ $(setter) a b' ifoldM f e a = do r <- L.foldM f e ( $(getter) a) return r ifoldr f e a = L.foldr f e ( $(getter) a) ifoldl f e a = L.foldl f e ( $(getter) a) ifoldl' f e a = L.foldl' f e ( $(getter) a) ilength d a = L.length ( $(getter) a) |] -- | Generates convenience function for iterating over a single object. self_iterable typA = [d| instance Iterable $(typA) $(typA) where imapM f a = f a ifoldM f e a = f e a ifoldr f e a = f a e ifoldl f e a = f e a ifoldl' f e a = f e a ilength d a = 1 |] --self_iterable typA = gen_iterable typA typA [e| id |] [e| L.singleton |] -- This works: -- ilength a = L.length ( $(getter) a) -- | Generates a transitive instance of `Iterable` between $typA and $typC, -- assuming existence of `Iterable` $typA $typB, and `Iterable` $typB $typC. trans_iterable typA typB typC = [d| instance Iterable $(typA) $(typC) where imapM f a = (imapM :: (Monad m) => ( $(typB) -> m $(typB) ) -> $(typA) -> m $(typA) ) (imapM f) a imap f a = (imap :: ( $(typB) -> $(typB) ) -> $(typA) -> $(typA) ) (imap f) a ifoldM f e a = (ifoldM :: (Monad m) => (c -> $(typB) -> m c) -> c -> $(typA) -> m c ) (ifoldM f) e a ifoldr f e a = (ifoldr :: ($(typB) -> c -> c) -> c -> $(typA) -> c ) (\bb cc -> ifoldr f cc bb) e a ifoldl f e a = (ifoldl :: (c -> $(typB) -> c) -> c -> $(typA) -> c ) (ifoldl f) e a ifoldl' f e a = (ifoldl' :: (c -> $(typB) -> c) -> c -> $(typA) -> c ) (ifoldl' f) e a ilength _ a = (ifoldl' :: (c -> $(typB) -> c) -> c -> $(typA) -> c ) (\a b-> a + ilength (undefined :: $(typC)) b) 0 a |] -- How to make this work: -- ilength a = (ifoldl' :: (Int -> $(typB) -> Int) -> Int -> $(typA) -> Int ) (\i b -> i+(ilength :: Iterable $(typB) $(typC) => $(typB) -> Int) b) 0 a