Scrap Your Boilerplate /~simonpj/papers/hmap
-
Upload
cornelius-manning -
Category
Documents
-
view
218 -
download
0
Transcript of Scrap Your Boilerplate /~simonpj/papers/hmap
Scrap Your BoilerplateScrap Your Boilerplate
http://research.microsoft.com/~http://research.microsoft.com/~simonpj/papers/hmap/simonpj/papers/hmap/
OutlineOutline
Boilerplate codeBoilerplate code IdeaIdea Recursively applyRecursively apply Generic transformGeneric transform
• ““Casting” solutionCasting” solution• AOP solutionAOP solution
Visitor patternVisitor pattern Queries and monadic transformationsQueries and monadic transformations Boilerplate 2 & 3Boilerplate 2 & 3
Boilerplate CodeBoilerplate Codedata Company = C [Dept]data Dept = D Name Manager [SubUnit]data SubUnit = PU Employee | DU Deptdata Employee = E Person Salarydata Person = P Name Addressdata Salary = S Floattype Manager = Employeetype Name = Stringtype Address = String
increase :: Float -> Company -> Companyincrease k (C ds) = C (map (incD k) ds)
incD k (D nm mgr us) = D nm (incE k mgr) (map (incU k) us)incU k (PU e) = PU (incE k e)incU k (DU d) = DU (incD k d)incE k (E p s) = E p (incS k s)incS k (S s) = S (s * (1 + k))
Boilerplate
IdeaIdea
Apply a generic transformApply a generic transform• Replacing incD, incU, incE and incSReplacing incD, incU, incE and incS
Apply the transformApply the transform• RecursivelyRecursively• BlindlyBlindly
Only affect the items we wantOnly affect the items we want Increase k =Increase k =
everywhere $ mkT $ incS k everywhere $ mkT $ incS kGenerate a generic
transformRecursively, blindly
apply
Recursively applyRecursively apply One-layer traversalOne-layer traversal class Typeable a => Term a whereclass Typeable a => Term a where
gmapT :: (forall b. Term b => b -> b) gmapT :: (forall b. Term b => b -> b) -> a -> a -> a -> a
instance Term Employee whereinstance Term Employee where gmapT f (E per sal) = E (f per) (f sal) gmapT f (E per sal) = E (f per) (f sal)
instance Term Bool whereinstance Term Bool where gmapT f x = x gmapT f x = x
instance Term a => Term [a] whereinstance Term a => Term [a] where gmapT f [] gmapT f [] = []= [] gmapT f (x:xs) gmapT f (x:xs) = f x : f xs= f x : f xs
Recursively apply (cont.)Recursively apply (cont.)
everywhere :: Term aeverywhere :: Term a => (forall b. Term b => b -> b) => (forall b. Term b => b -> b) -> a -> a -> a -> a
everywhere f x =everywhere f x = f $ gmapT (everywhere f) x f $ gmapT (everywhere f) x
everywhere’ f x =everywhere’ f x = gmapT (everywhere’ f) (f x) gmapT (everywhere’ f) (f x)
Recursively apply (cont.)Recursively apply (cont.)
Every involved data type have to be an iEvery involved data type have to be an instance of Termnstance of Term
The instantiation can be generated autoThe instantiation can be generated automaticallymatically• ““deriving” in GHC 6.4deriving” in GHC 6.4
Different combinations of gmapT lead to Different combinations of gmapT lead to different traversal policydifferent traversal policy
Rank-2 types, signature always neededRank-2 types, signature always needed
““Casting” SolutionCasting” Solution
class Typeableclass Typeable cast :: (Typeable a, Typeable b)cast :: (Typeable a, Typeable b)
=> a -> Maybe b => a -> Maybe b• (cast ‘a’) :: Maybe Char(cast ‘a’) :: Maybe Char
Just ‘a’Just ‘a’• (cast ‘a’) :: Maybe Bool(cast ‘a’) :: Maybe Bool
NothingNothing• (cast True) :: Maybe Bool(cast True) :: Maybe Bool
Just TrueJust True
““Casting” Solution (cont.)Casting” Solution (cont.)
mkT: generate a generic transformmkT: generate a generic transform• When the applied parameter is what we expWhen the applied parameter is what we exp
ect, apply the transformect, apply the transform• Otherwise, work like idOtherwise, work like id
mkT :: (Typeable a, Typeable b)mkT :: (Typeable a, Typeable b) => (b -> b) -> a -> a => (b -> b) -> a -> a
mkT f = case cast f ofmkT f = case cast f ofJust gJust g -> g-> gNothingNothing -> id-> id
““Casting” Solution (cont.)Casting” Solution (cont.)
Need a language supported “cast”Need a language supported “cast”• GHC 6.4 internal/libraryGHC 6.4 internal/library• Data.Typeable: Typeable, castData.Typeable: Typeable, cast• Data.Generics.Basic: Data (Term)Data.Generics.Basic: Data (Term)
Cannot write transformation directly, alCannot write transformation directly, always generated by mkTways generated by mkT
AOP SolutionAOP Solution
incS is itself the (dummy) generic transformincS is itself the (dummy) generic transform Decide whether the transform is applied or not Decide whether the transform is applied or not
by adviceby advice incS :: Float -> a -> aincS :: Float -> a -> a
incS k = idincS k = id advice around {incS k} (arg::Salary) =advice around {incS k} (arg::Salary) =
case arg of (S x) -> (S x*(1+k)) case arg of (S x) -> (S x*(1+k)) increase k = everywhere $ incS kincrease k = everywhere $ incS k
AOP Solution (cont.)AOP Solution (cont.)
type Salary = Floattype Salary = Float In “casting” solution, every field with tIn “casting” solution, every field with t
ype Float will be multiplied (including heype Float will be multiplied (including height, weight, etc.)ight, weight, etc.)
In AOP Solution (?):In AOP Solution (?):advice around {incS k} (arg::Salary)advice around {incS k} (arg::Salary) = arg * (1 + k) = arg * (1 + k)
AOP Solution (cont.)AOP Solution (cont.)
Less boilerplate codeLess boilerplate code Less data type for distinguishing meaninLess data type for distinguishing meanin
g of datag of data Dummy transformation, real work is donDummy transformation, real work is don
e by advicese by advices• idT = ididT = id
advice around {idT} (arg::Salary) =advice around {idT} (arg::Salary) = incS k arg incS k argincrease = everywhere idTincrease = everywhere idT
Visitor patternVisitor pattern
Visitor pattern (cont.)Visitor pattern (cont.)
Widely used in single-dispatch OO Widely used in single-dispatch OO languageslanguages
Visited object do the recursionVisited object do the recursion Decide whether the transform is Decide whether the transform is
applied or not by dispatchingapplied or not by dispatching
Queries and monadic transformQueries and monadic transform
class Typeable a => Term a whereclass Typeable a => Term a where gmapT :: (forall b. Term b => b -> b) gmapT :: (forall b. Term b => b -> b) -> a -> a -> a -> a gmapQ :: (forall b. Term b=> b -> r) gmapQ :: (forall b. Term b=> b -> r) -> a -> [r] -> a -> [r] gmapM :: Monad m => gmapM :: Monad m => (forall b. Term b -> m a) -> a -> m a (forall b. Term b -> m a) -> a -> m a-- special cases of gfoldl-- special cases of gfoldl
everything :: Term a => (r -> r -> r)everything :: Term a => (r -> r -> r) -> (forall a. Term a => a -> r) -> a -> r -> (forall a. Term a => a -> r) -> a -> r
everywhereM :: (b -> m b) -> a -> m aeverywhereM :: (b -> m b) -> a -> m a
Queries and monadic transform Queries and monadic transform (cont.)(cont.)
(r `mkQ` q) a = case cast a of(r `mkQ` q) a = case cast a ofJust bJust b -> q b-> q bNothingNothing -> r-> r
mkM f = case cast f ofmkM f = case cast f ofJust gJust g -> g-> gNothingNothing -> return-> return
totalBill = everything (+) (0 `mkQ` billS)totalBill = everything (+) (0 `mkQ` billS) dbSalaries = everywhereM (mkM lookupE)dbSalaries = everywhereM (mkM lookupE)
Queries and monadic transform Queries and monadic transform (cont.)(cont.)
queryS _ = 0 :: FloatqueryS _ = 0 :: Float advice around {queryS} (arg::Salary) = case arg advice around {queryS} (arg::Salary) = case arg
of S s -> sof S s -> s totalBill = everything (+) queryStotalBill = everything (+) queryS
transM _ = returntransM _ = return advice around {transM} (arg::Employee)advice around {transM} (arg::Employee)
= case arg of E p@(P n _) _ -> = case arg of E p@(P n _) _ -> do s <- dbLookup n; return $ E p s do s <- dbLookup n; return $ E p s
dbSalaries = everywhereM transMdbSalaries = everywhereM transM
Boilerplate 2Boilerplate 2
Reflection: Already in GHC 6.4 libraryReflection: Already in GHC 6.4 library Generic map, zipGeneric map, zip extQ, extT, extMextQ, extT, extM
• Extend a generic query/transformation/ moExtend a generic query/transformation/ monadic transformation by a type-specific casenadic transformation by a type-specific case
• GHC 6.4, Data.Generics.AliasGHC 6.4, Data.Generics.Alias
Boilerplate 3Boilerplate 3
Generalizing queries on Data (Term)Generalizing queries on Data (Term)• gsize :: Data a => a -> Intgsize :: Data a => a -> Int• gsize t = gsize_def `extQ` name_sizegsize t = gsize_def `extQ` name_size
`extQ` phone_size `extQ` phone_size• class Size a whereclass Size a where
gsize :: a -> Int gsize :: a -> Int instance Data t => Size t whereinstance Data t => Size t where
gsize t = 1 + sum (gmapQ gsize t) gsize t = 1 + sum (gmapQ gsize t) class Size a => Data a where …class Size a => Data a where …
This belongs to the SYB “library”Needed for “every” query
Boilerplate 3 (cont.)Boilerplate 3 (cont.)
Need “type variable over classes”Need “type variable over classes”• Can be encoded straightforwardly in standard HaskCan be encoded straightforwardly in standard Hask
ellell API of Data changedAPI of Data changed AOP solution cleanerAOP solution cleaner
• gsize t = 1 + sum (gmapQ gsize t)gsize t = 1 + sum (gmapQ gsize t)• advice around {gsize} (arg::Name) =advice around {gsize} (arg::Name) =
case arg of (N _) -> 1 case arg of (N _) -> 1• advice around {gsize} (arg::PhoneNumber) =advice around {gsize} (arg::PhoneNumber) =
length arg -- another special case length arg -- another special case