diff --git a/polyplate/Data/Generics/Polyplate.hs b/polyplate/Data/Generics/Polyplate.hs index 472b3eb..22b6f36 100644 --- a/polyplate/Data/Generics/Polyplate.hs +++ b/polyplate/Data/Generics/Polyplate.hs @@ -27,7 +27,7 @@ with this program. If not, see . -- Instances of the PolyplateM type-class /can/ be written manually but it's not -- advised. Instead, you should use functions in the "GenPolyplate" module to automatically -- generate source files with the appropriate instances. -module Data.Generics.Polyplate (PolyplateM(..), Polyplate(..), +module Data.Generics.Polyplate (PolyplateMRoute(..), PolyplateM(..), Polyplate(..), PolyplateSpine(..), FullSpine(..), transformSpine, transformSpineFull, trimTree, makeRecurseM, RecurseM, makeRecurse, Recurse, makeDescendM, DescendM, makeDescend, Descend, @@ -38,10 +38,11 @@ module Data.Generics.Polyplate (PolyplateM(..), Polyplate(..), ExtOpQ, extOpQ, OneOpQ, TwoOpQ) where import Control.Monad.Identity - import Data.Maybe import Data.Tree +import Data.Generics.Polyplate.Route + -- | The main Polyplate type-class. -- -- The first parameter is the larger\/outer type on which you want to operate. @@ -77,8 +78,20 @@ import Data.Tree -- -- Generally you will not use this function or type-class directly, but will instead -- use the helper functions lower down in this module. -class Monad m => PolyplateM t o o' m where - transformM :: o -> o' -> t -> m t -- TODO add routes +class Monad m => PolyplateMRoute t o o' m outer where + transformMRoute :: o -> o' -> (t, Route t outer) -> m t + +class (Monad m) => PolyplateM t o o' m where + transformM :: o -> o' -> t -> m t + +instance forall t ro o ro' o' m. (Monad m + , PolyplateMRoute t o o' m () + , ConvertOpsToIgnoreRoute ro o + , ConvertOpsToIgnoreRoute ro' o') => PolyplateM t ro ro' m where + transformM o o' t = transformMRoute (convertOpsToIgnoreRoute o) + (convertOpsToIgnoreRoute o') + (t, error "transformM" :: Route t ()) + -- List of use cases for the Polyplate type-class, to try to decide best on its -- necessary functions: @@ -123,32 +136,12 @@ transformSpineFull def o o' x fromJust' (Just x) = x fromJust' _ = error "transformSpineFull: internal error" -class ConvertSpineOpsToFull a o o' | a o -> o' where - convertSpineOpsToFull :: a -> o -> o' - -instance ConvertSpineOpsToFull a () (FullSpine a) where - convertSpineOpsToFull def _ = FullSpine def - -instance ConvertSpineOpsToFull b r r' => ConvertSpineOpsToFull b (a, r) (a, r') where - convertSpineOpsToFull def (f, r) = (f, convertSpineOpsToFull def r) - trimTree :: Tree (Maybe a) -> Maybe (Tree (Maybe a)) trimTree tr | isNothing (rootLabel tr) && null trimmedChildren = Nothing | otherwise = Just (Node (rootLabel tr) trimmedChildren) where trimmedChildren = mapMaybe trimTree (subForest tr) --- | A helper class to convert non-monadic transformations into monadic ones in --- the Identity monad. -class ConvertOpsToIdentity o o' | o -> o' where - convertOpsToIdentity :: o -> o' - -instance ConvertOpsToIdentity () () where - convertOpsToIdentity = id - -instance ConvertOpsToIdentity r r' => ConvertOpsToIdentity (a -> a, r) (a -> Identity a, r') where - convertOpsToIdentity (f, r) = (return . f, convertOpsToIdentity r) - -- | A non-monadic equivalent of PolyplateM. All ops sets are of the form: -- -- > (a -> a, (b -> b, ())) @@ -260,3 +253,41 @@ type TwoOp s t = ExtOp (ExtOp BaseOp s) t type TwoOpQ a s t = ExtOpQ a (ExtOpQ a BaseOp s) t +-- {{{ Various type-level programming ops conversions: + +-- | A helper class to convert non-monadic transformations into monadic ones in +-- the Identity monad. +class ConvertOpsToIdentity o o' | o -> o' where + convertOpsToIdentity :: o -> o' + +instance ConvertOpsToIdentity () () where + convertOpsToIdentity = id + +instance ConvertOpsToIdentity r r' => ConvertOpsToIdentity (a -> a, r) (a -> Identity a, r') where + convertOpsToIdentity (f, r) = (return . f, convertOpsToIdentity r) + +-- | A helper class to convert operation lists to have FullSpine at their base +-- rather than BaseOp +class ConvertSpineOpsToFull a o o' | a o -> o' where + convertSpineOpsToFull :: a -> o -> o' + +instance ConvertSpineOpsToFull a () (FullSpine a) where + convertSpineOpsToFull def _ = FullSpine def + +instance ConvertSpineOpsToFull b r r' => ConvertSpineOpsToFull b (a, r) (a, r') where + convertSpineOpsToFull def (f, r) = (f, convertSpineOpsToFull def r) + +-- | A helper class to convert operations not expecting a route to those that ignore +-- the route (which will have the unit type as its outer type). +class ConvertOpsToIgnoreRoute o o' | o -> o' where + convertOpsToIgnoreRoute :: o -> o' + +instance ConvertOpsToIgnoreRoute () () where + convertOpsToIgnoreRoute = id + +instance ConvertOpsToIgnoreRoute r r' => + ConvertOpsToIgnoreRoute (t -> m t, r) ((t, Route t ()) -> m t, r') where + convertOpsToIgnoreRoute (f, r) = (f . fst, convertOpsToIgnoreRoute r) + + +-- }}} diff --git a/polyplate/Data/Generics/Polyplate/GenInstances.hs b/polyplate/Data/Generics/Polyplate/GenInstances.hs index c772231..af47ba8 100644 --- a/polyplate/Data/Generics/Polyplate/GenInstances.hs +++ b/polyplate/Data/Generics/Polyplate/GenInstances.hs @@ -102,10 +102,9 @@ genMapInstance k v modify (Map.insert tk (show $ typeOf m, Detailed (DataBox m) [DataBox (k, v), DataBox k, DataBox v] (\(funcSameType, funcNewType) -> - [funcSameType ++ " () ops v = do" - ," keys <- mapM (" ++ funcNewType ++ " ops () . fst) (Map.toList v)" - ," vals <- mapM (" ++ funcNewType ++ " ops () . snd) (Map.toList v)" - ," return (Map.fromList (zip keys vals))" + [funcSameType ++ " () ops (v, r) = let mns = zip (Map.toList v) (map ((r @->) . routeDataMap) [0..]) in" + ," do m <- mapM (" ++ funcNewType ++ " ops ()) mns" + ," return (Map.fromList m)" ]) (\(funcSameType, funcNewType) -> [funcSameType ++ " () ops q v = Node q (map (" @@ -127,9 +126,9 @@ genSetInstance x modify (Map.insert tk (show $ typeOf s, Detailed (DataBox s) [DataBox x] (\(funcSameType, funcNewType) -> - [funcSameType ++ " () ops v = do" - ," vals <- mapM (" ++ funcNewType ++ " ops ()) (Set.toList v)" - ," return (Set.fromList vals)" + [funcSameType ++ " () ops (v, r) = let sns = zip (Set.toList v) (map ((r @->) . routeDataSet) [0..]) in" + ," do s <- mapM (" ++ funcNewType ++ " ops ()) sns" + ," return (Set.fromList s)" ]) (\(funcSameType, funcNewType) -> [funcSameType ++ " () ops q v = Node q (map (" @@ -287,33 +286,33 @@ instancesFrom genOverlapped genClass boxes w -- sets. The class name will be the same as genInst. contextSameType :: String -> String -> String contextSameType ops0 ops1 = case genClass of - GenOneClass -> "PolyplateM (" ++ wName ++ ") " ++ ops0 ++ " " ++ ops1 ++ " m" - GenClassPerType -> "PolyplateM" ++ wMunged ++" " ++ ops0 ++ " " ++ ops1 ++ " m" - GenSlowDelegate -> "PolyplateM' m " ++ ops0 ++ " " ++ ops1 ++ " (" ++ wName ++ ")" + GenOneClass -> "PolyplateMRoute (" ++ wName ++ ") " ++ ops0 ++ " " ++ ops1 ++ " m outer" + GenClassPerType -> "PolyplateMRoute" ++ wMunged ++" " ++ ops0 ++ " " ++ ops1 ++ " m outer" + GenSlowDelegate -> "PolyplateMRoute' m " ++ ops0 ++ " " ++ ops1 ++ " (" ++ wName ++ ") outer" -- Generates the name of an instance for a different type (for processing children). -- This will be PolyplateM or PolyplateM'. contextNewType :: String -> String -> String -> String contextNewType cName ops0 ops1 = case genClass of - GenOneClass -> "PolyplateM (" ++ cName ++ ") " ++ ops0 ++ " " ++ ops1 ++ " m" - GenClassPerType -> "PolyplateM (" ++ cName ++ ") " ++ ops0 ++ " " ++ ops1 ++ " m" - GenSlowDelegate -> "PolyplateM' m " ++ ops0 ++ " " ++ ops1 ++ " (" ++ cName ++ ")" + GenOneClass -> "PolyplateMRoute (" ++ cName ++ ") " ++ ops0 ++ " " ++ ops1 ++ " m outer" + GenClassPerType -> "PolyplateMRoute (" ++ cName ++ ") " ++ ops0 ++ " " ++ ops1 ++ " m outer" + GenSlowDelegate -> "PolyplateMRoute' m " ++ ops0 ++ " " ++ ops1 ++ " (" ++ cName ++ ") outer" -- The function to define in the body, and also to use for processing the same -- type. funcSameType :: String funcSameType = case genClass of - GenClassPerType -> "transformM" ++ wMunged - GenOneClass -> "transformM" - GenSlowDelegate -> "transformM'" + GenClassPerType -> "transformMRoute" ++ wMunged + GenOneClass -> "transformMRoute" + GenSlowDelegate -> "transformMRoute'" -- The function to use for processing other types funcNewType :: String funcNewType = case genClass of - GenClassPerType -> "transformM" - GenOneClass -> "transformM" - GenSlowDelegate -> "transformM'" + GenClassPerType -> "transformMRoute" + GenOneClass -> "transformMRoute" + GenSlowDelegate -> "transformMRoute'" -- | An instance that describes what to do when we have no transformations -- left to apply. You can pass it an override for the case of processing children @@ -327,21 +326,22 @@ instancesFrom genOverlapped genClass boxes w -- An algebraic type: apply to each child if we're following. then (concatMap constrCase wCtrs) -- A primitive (or non-represented) type: just return it. - else [funcSameType ++ " () _ v = return v"]) + else [funcSameType ++ " () _ (v,_) = return v"]) (\(_,f) -> f (funcSameType, funcNewType)) mdoChildren - ,genInst [] "()" "()" [funcSameType ++ " () () v = return v"] + ,genInst [] "()" "()" [funcSameType ++ " () () (v,_) = return v"] ,if genOverlapped == GenWithoutOverlapped then [] else genInst [ contextSameType "r" "ops" ] - "(a -> m a, r)" "ops" - [funcSameType ++ " (_, rest) ops v = " ++ funcSameType ++ " rest ops v"] + "((a, Route a outer) -> m a, r)" "ops" + [funcSameType ++ " (_, rest) ops vr = " ++ funcSameType ++ " rest ops vr"] ,if genClass == GenClassPerType - then ["class Monad m => PolyplateM" ++ wMunged ++ " o o' m where" - ," " ++ funcSameType ++ " :: o -> o' -> (" ++ wName ++ ") -> m (" ++ wName ++ ")" + then ["class Monad m => PolyplateMRoute" ++ wMunged ++ " o o' m outer where" + ," " ++ funcSameType ++ " :: o -> o' -> (" ++ wName + ++ ", Route (" ++ wName ++ ") outer) -> m (" ++ wName ++ ")" ,"" ,"instance (Monad m, " ++ contextSameType "o0" "o1" ++ ") =>" - ," PolyplateM (" ++ wName ++ ") o0 o1 m where" - ," transformM = " ++ funcSameType + ," PolyplateMRoute (" ++ wName ++ ") o0 o1 m outer where" + ," transformMRoute = " ++ funcSameType ] else [] ] @@ -361,10 +361,13 @@ instancesFrom genOverlapped genClass boxes w constrCase :: Constr -> [String] constrCase ctr = [ funcSameType ++ " () " ++ (if argNums == [] then "_" else "ops") ++ - " (" ++ ctrInput ++ ")" + " (" ++ ctrInput ++ ", " ++ (if argNums == [] then "_" else "rt") ++ ")" , " = do" ] ++ - [ " r" ++ show i ++ " <- " ++ funcNewType ++ " ops () a" ++ show i + [ " r" ++ show i ++ " <- " ++ funcNewType ++ " ops () (a" ++ show i + ++ ", rt @-> makeRoute [" ++ show i ++ "] " + ++ "(\\f (" ++ ctrMod ++ ") -> f b" ++ show i + ++ " >>= (\\b" ++ show i ++ " -> return (" ++ ctrMod ++ "))))" | i <- argNums] ++ [ " return (" ++ ctrResult ++ ")" ] @@ -374,6 +377,7 @@ instancesFrom genOverlapped genClass boxes w ctrName = modPrefix ++ ctrS makeCtr vs = ctrName ++ concatMap (" " ++) vs ctrInput = makeCtr ["a" ++ show i | i <- argNums] + ctrMod = makeCtr ["b" ++ show i | i <- argNums] ctrResult = makeCtr ["r" ++ show i | i <- argNums] @@ -383,7 +387,7 @@ instancesFrom genOverlapped genClass boxes w otherInst wKey containedKeys c cKey = if not shouldGen then [] else genInst context - ("((" ++ cName ++ ") -> m (" ++ cName ++ "), r)") + ("((" ++ cName ++ ", Route (" ++ cName ++ ") outer) -> m (" ++ cName ++ "), r)") "ops" impl where @@ -393,19 +397,19 @@ instancesFrom genOverlapped genClass boxes w | wKey == cKey = (True ,[] - ,[funcSameType ++ " (f, _) _ v = f v"]) + ,[funcSameType ++ " (f, _) _ vr = f vr"]) -- This type might contain the type that the transformation acts -- upon | cKey `Set.member` containedKeys = (True - ,[contextSameType "r" ("((" ++ cName ++ ") -> m (" ++ cName ++ "), ops)")] - ,[funcSameType ++ " (f, rest) ops v = " ++ funcSameType ++ " rest (f, ops) v"]) + ,[contextSameType "r" ("((" ++ cName ++ ", Route (" ++ cName ++ ") outer) -> m (" ++ cName ++ "), ops)")] + ,[funcSameType ++ " (f, rest) ops vr = " ++ funcSameType ++ " rest (f, ops) vr"]) -- This type can't contain the transformed type; just move on to the -- next transformation. | genOverlapped == GenWithoutOverlapped = (True ,[contextSameType "r" "ops"] - ,[funcSameType ++ " (_, rest) ops v = " ++ funcSameType ++ " rest ops v"]) + ,[funcSameType ++ " (_, rest) ops vr = " ++ funcSameType ++ " rest ops vr"]) -- This is covered by one big overlapping instance: | otherwise = (False,[],[]) diff --git a/pregen/GenNavAST.hs b/pregen/GenNavAST.hs index 98fe1cc..daeb661 100644 --- a/pregen/GenNavAST.hs +++ b/pregen/GenNavAST.hs @@ -176,18 +176,19 @@ main = do | otherwise = (reverse . takeWhile (/= '/') . drop 3 . reverse) $ moduleFileName - header useTree moduleName = - ["{-# OPTIONS_GHC -Werror -fwarn-overlapping-patterns -fwarn-unused-matches -fwarn-unused-binds -fwarn-incomplete-patterns #-}" + header isSpine moduleName = + ["{-# OPTIONS_GHC -Werror -fwarn-overlapping-patterns -fwarn-unused-matches -fwarn-unused-binds #-}" ,"-- | This module is auto-generated by Polyplate. DO NOT EDIT." ,"module " ++ moduleName ++ " where" ,"" ,"import Data.Generics.Polyplate" + ,if isSpine then "" else "import Data.Generics.Polyplate.Route" ,"" ,"import Data.Map (Map)" ,"import qualified Data.Map as Map" ,"import Data.Set (Set)" ,"import qualified Data.Set as Set" - ,if useTree then "import Data.Tree" else "" + ,if isSpine then "import Data.Tree" else "" ,"" ,"import qualified AST" ,"import qualified CompState"