packages feed

barbies-th 0.1.8 → 0.1.9

raw patch · 9 files changed

+210/−42 lines, 9 filesdep ~barbiesdep ~basedep ~template-haskellPVP ok

version bump matches the API change (PVP)

Dependency ranges changed: barbies, base, template-haskell

API changes (from Hackage documentation)

+ Barbies.TH: declareBareBWith :: DeclareBareBConfig -> DecsQ -> DecsQ
+ Barbies.TH: passthroughBareB :: DecsQ -> DecsQ
+ Barbies.TH.Config: DeclareBareBConfig :: [Name] -> (String -> Maybe String) -> (String -> Maybe String) -> (String -> String) -> DeclareBareBConfig
+ Barbies.TH.Config: [barbieName] :: DeclareBareBConfig -> String -> String
+ Barbies.TH.Config: [bareName] :: DeclareBareBConfig -> String -> Maybe String
+ Barbies.TH.Config: [coveredName] :: DeclareBareBConfig -> String -> Maybe String
+ Barbies.TH.Config: [friends] :: DeclareBareBConfig -> [Name]
+ Barbies.TH.Config: classic :: DeclareBareBConfig
+ Barbies.TH.Config: data DeclareBareBConfig
+ Barbies.TH.Config: passthrough :: DeclareBareBConfig

Files

+ .gitignore view
@@ -0,0 +1,4 @@+.stack-work/+*~+.ghc.environment.*+dist-newstyle/
CHANGELOG.md view
@@ -1,5 +1,9 @@ # Revision history for barbies-th +## 0.1.9++* Added `passthroughBareB`+ ## 0.1.8  * `declareBareB` can now generate nested barbies when multiple data declarations are passed (#7)
README.md view
@@ -33,9 +33,21 @@ instance ProductBC (Foo Covered) ``` -Typically you need the following extensions to make `declareBareB` work:+`passthroughBareB` generates a type synonym for the bare type,+preserving the semantics of the original definition. +```haskell+data FooB sw h = Foo+    { foo :: Wear sw h Int,+    , bar :: Wear sw h String+    }+type Foo = Foo Bare Identity+type FooH = Foo Covered ```++Typically you need the following extensions to make `declareBareB` work:++```haskell {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TypeFamilies #-}@@ -54,16 +66,20 @@ stock deriving does not work on HKDs. Instead, it transforms deriving clauses into standalone ones via the `Barbie` wrapper, as well as ones for the `Bare` counterpart. For example, -`data Foo = ... deriving (Show, Eq)`+```haskell+data Foo = ... deriving (Show, Eq)+```  generates -```+```haskell deriving instance Show (Foo Bare Identity) deriving instance Eq (Foo Bare Identity) deriving via Barbie (Foo Covered) h instance Show (Barbie (Foo Covered) h) => Show (Foo Covered h) deriving via Barbie (Foo Covered) h instance Eq (Barbie (Foo Covered) h) => Eq (Foo Covered h) ```++Note that `Barbies` module must be imported manually in order to make use of coercions.  Matryoshka barbies ----
barbies-th.cabal view
@@ -1,24 +1,34 @@ cabal-version:       2.4 name:                barbies-th-version:             0.1.8+version:             0.1.9 synopsis:            Create strippable HKD via TH description:         Please see Data.Barbie.TH-bug-reports:         https://github.com/fumieval/barbies-th+homepage:            https://github.com/fumieval/barbies-th+bug-reports:         https://github.com/fumieval/barbies-th/issues license:             BSD-3-Clause license-file:        LICENSE author:              Fumiaki Kinoshita maintainer:          fumiexcel@gmail.com-copyright:           Copyright (c) 2020 Fumiaki Kinoshita-category:            Data, Generics-extra-source-files:  CHANGELOG.md, README.md+copyright:           Copyright (c) 2021 Fumiaki Kinoshita+category:            Data, Generics, Data Structures+extra-source-files:+  CHANGELOG.md+  README.md+  cabal.project+  .gitignore +source-repository head+  type: git+  location: https://github.com/fumieval/barbies-th.git+ library   exposed-modules:     Barbies.TH+    Barbies.TH.Config     Data.Barbie.TH   other-extensions:    RankNTypes, PolyKinds, DataKinds, KindSignatures, TemplateHaskell, TypeFamilies-  build-depends:       base >= 4.12-    , template-haskell >= 2.14 && <2.17+  build-depends:       base >= 4.12 && <4.17+    , template-haskell >= 2.14 && <2.18     , barbies ^>= 2.0.1     , split ^>= 0.2   hs-source-dirs:      src@@ -28,6 +38,17 @@ test-suite th   type: exitcode-stdio-1.0   main-is: Main.hs+  hs-source-dirs:+      tests+  build-depends:+      base >=4.7 && <5+    , barbies+    , barbies-th+  default-language: Haskell2010++test-suite th-passthrough+  type: exitcode-stdio-1.0+  main-is: passthrough.hs   hs-source-dirs:       tests   build-depends:
+ cabal.project view
@@ -0,0 +1,3 @@+packages: .+allow-newer:+  split:base
src/Barbies/TH.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE CPP #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE PolyKinds #-}@@ -12,12 +13,16 @@ {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE UndecidableInstances #-} {-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE PatternSynonyms #-}+{-# LANGUAGE RecordWildCards #-} module Barbies.TH (FieldNamesB(..)   , LensB(..)   , getLensB   , AccessorsB(..)   , declareBareB+  , declareBareBWith   , declareBareBWithOtherBarbies+  , passthroughBareB   ) where  import Language.Haskell.TH hiding (cxt)@@ -28,14 +33,14 @@ import Barbies import Barbies.Constraints import Barbies.Bare+import Barbies.TH.Config import Data.Functor.Product import GHC.Generics (Generic) import Control.Applicative import Data.Functor.Identity (Identity(..)) import Data.Functor.Compose (Compose(..)) import Data.List.Split-import Data.Maybe (mapMaybe)-import Data.Bool (bool)+import Data.Maybe  -- | A pair of a getter and a setter -- Not van Laarhoven to avoid dictionary passing@@ -48,10 +53,12 @@ nestLensB l (LensB lv ls) =   LensB (lv . snd . l) (\n h -> let (s, x) = l h in s (ls n x)) +-- | Obtain a van-laarhoven lens (compatible with the lens library) from 'LensB' getLensB :: Functor f => LensB b a -> (h a -> f (h a)) -> b h -> f (b h) getLensB (LensB v s) f b = (\x -> s x b) <$> f (v b) {-# INLINE getLensB #-} +-- | The class of higher-kinded datatypes where lenses can be defined class AccessorsB b where   -- | A collection of lenses (getter-setter pairs)   baccessors :: b (LensB b)@@ -75,18 +82,29 @@ -- @data User t f = User { uid :: Wear t f Int, name :: Wear t f String }@ -- declareBareB :: DecsQ -> DecsQ-declareBareB = declareBareBWithOtherBarbies []+declareBareB = declareBareBWith classic +-- | Defines a synonym for the bare type with the same name.+-- The strippable definition is suffixed by B, and the covered type is suffixed by H.+passthroughBareB :: DecsQ -> DecsQ+passthroughBareB = declareBareBWith passthrough+ -- | Like 'declareBareB' except that one can specify the 'Name's of other -- barbies. Members with these types won't be wrapped with 'Wear'. declareBareBWithOtherBarbies :: [Name] -> DecsQ -> DecsQ-declareBareBWithOtherBarbies friends decsQ = do+declareBareBWithOtherBarbies xs = declareBareBWith classic { friends = xs }++-- | Generate a higher-kinded data declaration using a custom config+declareBareBWith :: DeclareBareBConfig -> DecsQ -> DecsQ+declareBareBWith DeclareBareBConfig{..} decsQ = do   decs <- decsQ-  let newTypeNames = dataDecNames decs-  decs' <- traverse (go (newTypeNames <> friends)) decs+  let otherBarbieNames = [ (k, mkName $ barbieName $ nameBase k) | k <- dataDecNames decs ]+          ++ map (\x -> (x, x)) friends+  decs' <- traverse (go otherBarbieNames) decs   return $ concat decs'   where-    go otherBarbieNames (DataD _ dataName tvbs _ [con@(RecC nDataCon mangledfields)] classes) = do+    go otherBarbieNames (DataD _ dataName0 tvbs _ [con@(RecC nDataCon mangledfields)] classes) = do+      let dataName = mkName $ barbieName $ nameBase dataName0       let fields = [(unmangle name, c, t) | (name, c, t) <- mangledfields]       nSwitch <- newName "sw"       nWrap <- newName "h"@@ -95,12 +113,12 @@       -- 'mapMembers' applies one of two functions to elements of a list       -- according to whether or not they align with another barbie       let otherBarbieMask = [ case t of-                                ConT n | n `elem` otherBarbieNames -> True-                                _ -> False+                                ConT n | Just v <- lookup n otherBarbieNames -> Just v+                                _ -> Nothing                             | (_, _, t) <- fields                             ]       let mapMembers :: (b -> c) -> (b -> c) -> [b] -> [c]-          mapMembers normal otherBarbie = zipWith (bool normal otherBarbie) otherBarbieMask+          mapMembers normal otherBarbie = zipWith (maybe normal (const otherBarbie)) otherBarbieMask       nData <- newName "b"       nConstr <- newName "c"       nX <- newName "x"@@ -109,7 +127,7 @@           -- field names for FieldNamesB           fieldNamesE = reconE $ mapMembers             (\(name,_,_) -> [|Const $ fromString $(litE $ StringL $ nameBase name)|])-            (const [|bfieldNames|])+            (\_ -> [|bfieldNames|])             fields           accessors = reconE $ mapMembers             (\name -> [|LensB@@ -132,25 +150,36 @@           -- Turn TyVarBndr into just a Name such that we can           -- reconstruct the constructor applied to already-present           -- type variables below.+#if MIN_VERSION_template_haskell(2,17,0)+          varName (PlainTV n _) = n+          varName (KindedTV n _ _) = n+#else           varName (PlainTV n) = n           varName (KindedTV n _) = n+#endif            -- The type name as present originally along with its type           -- variables.           vanillaType = foldl' appT (conT dataName) (varT . varName <$> tvbs) -          -- max arity = 62-          typeChunks = chunksOf 62 (mapMembers-              (\t -> varT nConstr `appT` t)-              (\t -> [t| AllB $(varT nConstr) ($t Covered)|])-              [pure t | (_, _, t) <- fields]-            )+      -- bare/covered types+      bareType <- [t| $(vanillaType) Bare Identity |]+      coveredType <- [t| $(vanillaType) Covered |]++      -- max arity = 62+      let typeChunks = chunksOf 62+            [ case mask of+              Just t' -> [t| AllB $(varT nConstr) ($(conT t') Covered)|]+              Nothing -> varT nConstr `appT` pure t+            | ((_, _, t), mask) <- zip fields otherBarbieMask+            ]           mkConstraints ps = foldl appT (tupleT $ length ps) ps           allConstr = case typeChunks of             [ps] -> mkConstraints ps             pss -> mkConstraints $ map mkConstraints pss -      let datC = vanillaType `appT` conT ''Covered++      let datC = pure coveredType       decs <- [d|         instance BareB $(vanillaType) where           bcover $(conP nDataCon $ map varP xs)@@ -159,12 +188,12 @@           bstrip $(conP nDataCon $ map varP xs)             = $(reconE $ mapMembers (appE (varE 'runIdentity)) (appE (varE 'bstrip)) (varE <$> xs))           {-# INLINE bstrip #-}-        instance FieldNamesB $(datC) where bfieldNames = $(fieldNamesE)-        instance AccessorsB $(datC) where baccessors = $(accessors)-        instance FunctorB $(datC) where+        instance FieldNamesB $(pure coveredType) where bfieldNames = $(fieldNamesE)+        instance AccessorsB $(pure coveredType) where baccessors = $(accessors)+        instance FunctorB $(pure coveredType) where           bmap f $(conP nDataCon $ map varP xs)             = $(reconE (mapMembers (appE (varE 'f)) (appE [|bmap f|]) (varE <$> xs)))-        instance DistributiveB $(datC) where+        instance DistributiveB $(pure coveredType) where           bdistribute fb = $(reconE $               -- TODO: NoFieldSelectors               mapMembers@@ -172,22 +201,22 @@                 (\fd -> [| bdistribute ($fd <$> fb) |])                 [varE fd | (fd, _, _) <- fields]             )-        instance TraversableB $(datC) where+        instance TraversableB $(pure coveredType) where           btraverse f $(conP nDataCon $ map varP xs) = $(fst $ foldl'               (\(l, op) r -> (infixE (Just l) (varE op) (Just r), '(<*>)))               (conE nDataCon, '(<$>))               (mapMembers (appE (varE 'f)) (\x -> [|btraverse f $x|]) (varE <$> xs))             )           {-# INLINE btraverse #-}-        instance ConstraintsB $(datC) where-          type AllB $(varT nConstr) $(datC) = $(allConstr)+        instance ConstraintsB $(pure coveredType) where+          type AllB $(varT nConstr) $(pure coveredType) = $(allConstr)           baddDicts $(conP nDataCon $ map varP xs)             = $(reconE $ mapMembers                  (\x -> [|Pair Dict $x|])                  (\x -> [|baddDicts $x|])                  (varE <$> xs)                )-        instance ApplicativeB $(datC) where+        instance ApplicativeB $(pure coveredType) where           bpure $(varP nX) = $(reconE $ mapMembers                                  (const (varE nX))                                  (const [|bpure $(varE nX)|])@@ -195,13 +224,14 @@                               )           bprod $(conP nDataCon $ map varP xs) $(conP nDataCon $ map varP ys) = $(foldl'             (\r (isOtherBarbie, x, y) ->-              if isOtherBarbie+              if isJust isOtherBarbie                 then [|$r (bprod $(varE x) $(varE y))|]                 else [|$r (Pair $(varE x) $(varE y))|])             (conE nDataCon) (zip3 otherBarbieMask xs ys))         |]       -- strip deriving Generic       let classes' = map (\(DerivClause strat cs) -> fmap (DerivClause strat) $ partition (== ConT ''Generic) cs) classes+       -- For the covered type, derive instances via 'Barbie' wrapper instead.       coverDrvs <- traverse (\cls ->         [d|deriving via Barbie $(datC) $(varT nWrap)@@ -209,14 +239,20 @@         [ pure t | (_, DerivClause _ preds) <- classes', t <- preds ]       -- Redefine instances of the bare type with the original strategy       bareDrvs <- traverse (\(strat, cls) ->-        standaloneDerivWithStrategyD strat (pure []) [t|$(cls) ($(vanillaType) Bare Identity)|])+        standaloneDerivWithStrategyD strat (pure []) [t|$(cls) $(pure bareType)|])         [ (strat, pure t) | (_, DerivClause strat preds) <- classes', t <- preds ]       return $ DataD [] dataName+#if MIN_VERSION_template_haskell(2,17,0)+        (tvbs ++ [PlainTV nSwitch (), PlainTV nWrap ()])+#else         (tvbs ++ [PlainTV nSwitch, PlainTV nWrap])+#endif         Nothing         [transformed]         [DerivClause Nothing $ concatMap fst classes']         : decs ++ concat coverDrvs ++ bareDrvs+        ++ [ TySynD (mkName name) tvbs bareType | name <- maybeToList $ bareName $ nameBase dataName0]+        ++ [ TySynD (mkName name) tvbs coveredType | name <- maybeToList $ coveredName $ nameBase dataName0]     go _ d = pure [d]  dataDecNames :: [Dec] -> [Name]@@ -230,7 +266,7 @@ varNames :: String -> [VarBangType] -> [Name] varNames p vbt = [mkName (p ++ nameBase v) | (v, _, _) <- vbt] -transformCon :: [Name] -- ^ Names of other barbies+transformCon :: [(Name, Name)] -- ^ Names of other barbies   -> Name -- ^ switch variable   -> Name -- ^ wrapper variable   -> Con -- ^ original constructor@@ -241,8 +277,8 @@   | (v, b, t) <- xs   , let     t' = case t of-      ConT n | n `elem` otherBarbieNames ->-        ConT n `AppT` VarT switchName `AppT` VarT wrapperName+      ConT n | Just n' <- lookup n otherBarbieNames ->+        ConT n' `AppT` VarT switchName `AppT` VarT wrapperName       _ -> ConT ''Wear `AppT` VarT switchName `AppT` VarT wrapperName `AppT` t   ] transformCon otherBarbieNames var w (ForallC tvbs cxt con) =
+ src/Barbies/TH/Config.hs view
@@ -0,0 +1,36 @@+module Barbies.TH.Config+  ( DeclareBareBConfig(..)+  , classic+  , passthrough+  ) where+import Language.Haskell.TH++-- | Keep it in a separate module until NoFieldSelectors gets widespread+data DeclareBareBConfig = DeclareBareBConfig+  { friends :: [Name] -- ^ Members with these types won't be wrapped with 'Wear'+  , bareName :: String -> Maybe String+  -- ^ generate a type synonym for the 'Barbies.Bare.Bare' type?+  , coveredName :: String -> Maybe String+  -- ^ generate a type synonym for the 'Barbies.Bare.Covered' type?+  , barbieName :: String -> String+  -- ^ modify the name of the datatype+  }++-- | Does not define any type synonyms+classic :: DeclareBareBConfig+classic = DeclareBareBConfig+  { friends = []+  , bareName = const Nothing+  , coveredName = const Nothing+  , barbieName = id+  }++-- | Defines a synonym for the bare type with the same name.+-- The strippable definition is suffixed by B, and the covered type is suffixed by H.+passthrough :: DeclareBareBConfig+passthrough = DeclareBareBConfig+  { friends = []+  , bareName = Just+  , coveredName = Just . (++"H")+  , barbieName = (++"B")+  }
tests/Main.hs view
@@ -19,7 +19,7 @@     { foo :: Int     , bar :: String     } deriving (Show, Eq, Generic)|]- + declareBareB [d|   data Inner = Inner     { inner :: Int
+ tests/passthrough.hs view
@@ -0,0 +1,48 @@+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE StandaloneDeriving #-}+{-# LANGUAGE DerivingVia #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE UndecidableInstances #-}+{-# OPTIONS -ddump-splices #-}+module Main where+import Barbies.TH+import GHC.Generics+import Barbies+import Barbies.Bare+import Data.Functor.Identity++passthroughBareB [d|+  data Foo = Foo+    { foo :: Int+    , bar :: String+    } deriving (Show, Eq, Generic)|]++passthroughBareB [d|+  data Inner = Inner+    { inner :: Int+    } deriving (Show, Eq, Generic)+  data Outer = Outer+    { outer :: Inner+    , other :: Bool+    } deriving (Show, Eq, Generic)+    |]++test_con :: FooH []+test_con = Foo+  { foo = [0]+  , bar = ["Haskell"]+  }++test_bare :: Inner+test_bare = Inner 0 :: InnerB Bare Identity++test_sel :: ([Int], [String])+test_sel = (foo test_con, bar test_con)++test_upd :: FooH []+test_upd = test_con { foo = [], bar = [] }++main = pure ()+