1 / 21

Scrap Your Boilerplate

Scrap Your Boilerplate. http://research.microsoft.com/~simonpj/papers/hmap/. Outline. Boilerplate code Idea Recursively apply Generic transform “ Casting ” solution AOP solution Visitor pattern Queries and monadic transformations Boilerplate 2 & 3. Boilerplate. Boilerplate Code.

yan
Télécharger la présentation

Scrap Your Boilerplate

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Scrap Your Boilerplate http://research.microsoft.com/~simonpj/papers/hmap/

  2. Outline • Boilerplate code • Idea • Recursively apply • Generic transform • “Casting” solution • AOP solution • Visitor pattern • Queries and monadic transformations • Boilerplate 2 & 3

  3. Boilerplate Boilerplate Code data 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))

  4. Idea • Apply a generic transform • Replacing incD, incU, incE and incS • Apply the transform • Recursively • Blindly • Only affect the items we want • Increase k = everywhere $ mkT $ incS k Recursively, blindly apply Generate a generic transform

  5. Recursively apply • One-layer traversal • class Typeable a => Term a where gmapT :: (forall b. Term b => b -> b) -> a -> a • instance Term Employee where gmapT f (E per sal) = E (f per) (f sal) • instance Term Bool where gmapT f x = x • instance Term a => Term [a] where gmapT f [] = [] gmapT f (x:xs) = f x : f xs

  6. Recursively apply (cont.) • everywhere :: Term a => (forall b. Term b => b -> b) -> a -> a • everywhere f x = f $ gmapT (everywhere f) x • everywhere’ f x = gmapT (everywhere’ f) (f x)

  7. Recursively apply (cont.) • Every involved data type have to be an instance of Term • The instantiation can be generated automatically • “deriving” in GHC 6.4 • Different combinations of gmapT lead to different traversal policy • Rank-2 types, signature always needed

  8. “Casting” Solution • class Typeable • cast :: (Typeable a, Typeable b) => a -> Maybe b • (cast ‘a’) :: Maybe Char • Just ‘a’ • (cast ‘a’) :: Maybe Bool • Nothing • (cast True) :: Maybe Bool • Just True

  9. “Casting” Solution (cont.) • mkT: generate a generic transform • When the applied parameter is what we expect, apply the transform • Otherwise, work like id • mkT :: (Typeable a, Typeable b) => (b -> b) -> a -> a • mkT f = case cast f of Just g -> g Nothing -> id

  10. “Casting” Solution (cont.) • Need a language supported “cast” • GHC 6.4 internal/library • Data.Typeable: Typeable, cast • Data.Generics.Basic: Data (Term) • Cannot write transformation directly, always generated by mkT

  11. AOP Solution • incS is itself the (dummy) generic transform • Decide whether the transform is applied or not by advice • incS :: Float -> a -> aincS k = id • advice around {incS k} (arg::Salary) = case arg of (S x) -> (S x*(1+k)) • increase k = everywhere $ incS k

  12. AOP Solution (cont.) • type Salary = Float • In “casting” solution, every field with type Float will be multiplied (including height, weight, etc.) • In AOP Solution (?):advice around {incS k} (arg::Salary) = arg * (1 + k)

  13. AOP Solution (cont.) • Less boilerplate code • Less data type for distinguishing meaning of data • Dummy transformation, real work is done by advices • idT = idadvice around {idT} (arg::Salary) = incS k argincrease = everywhere idT

  14. Visitor pattern

  15. Visitor pattern (cont.) • Widely used in single-dispatch OO languages • Visited object do the recursion • Decide whether the transform is applied or not by dispatching

  16. Queries and monadic transform • class Typeable a => Term a where gmapT :: (forall b. Term b => b -> b) -> a -> a gmapQ :: (forall b. Term b=> b -> r) -> a -> [r] gmapM :: Monad m => (forall b. Term b -> m a) -> a -> m a-- special cases of gfoldl • everything :: Term a => (r -> r -> r) -> (forall a. Term a => a -> r) -> a -> r • everywhereM :: (b -> m b) -> a -> m a

  17. Queries and monadic transform (cont.) • (r `mkQ` q) a = case cast a of Just b -> q b Nothing -> r • mkM f = case cast f of Just g -> g Nothing -> return • totalBill = everything (+) (0 `mkQ` billS) • dbSalaries = everywhereM (mkM lookupE)

  18. Queries and monadic transform (cont.) • queryS _ = 0 :: Float • advice around {queryS} (arg::Salary) = case arg of S s -> s • totalBill = everything (+) queryS • transM _ = return • advice around {transM} (arg::Employee) = case arg of E p@(P n _) _ -> do s <- dbLookup n; return $ E p s • dbSalaries = everywhereM transM

  19. Boilerplate 2 • Reflection: Already in GHC 6.4 library • Generic map, zip • extQ, extT, extM • Extend a generic query/transformation/ monadic transformation by a type-specific case • GHC 6.4, Data.Generics.Alias

  20. Boilerplate 3 • Generalizing queries on Data (Term) • gsize :: Data a => a -> Int • gsize t = gsize_def `extQ` name_size `extQ` phone_size • class Size a where gsize :: a -> Int • instance Data t => Size t where gsize t = 1 + sum (gmapQ gsize t) • class Size a => Data a where … Needed for “every” query This belongs to the SYB “library”

  21. Boilerplate 3 (cont.) • Need “type variable over classes” • Can be encoded straightforwardly in standard Haskell • API of Data changed • AOP solution cleaner • gsize t = 1 + sum (gmapQ gsize t) • advice around {gsize} (arg::Name) = case arg of (N _) -> 1 • advice around {gsize} (arg::PhoneNumber) = length arg -- another special case

More Related