packages feed

derive-lifted-instances 0.1.1 → 0.2

raw patch · 6 files changed

+95/−49 lines, 6 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

- Data.DeriveLiftedInstances.Internal: tvName :: TyVarBndr -> Name
- Data.DeriveLiftedInstances.Internal: varPat :: Name -> Q Pat
+ Data.DeriveLiftedInstances: [cst] :: Derivator -> Q Exp -> Q Exp
+ Data.DeriveLiftedInstances: [eff] :: Derivator -> Q Exp -> Q Exp
+ Data.DeriveLiftedInstances: [inp] :: Derivator -> Q Pat -> Q Pat
+ Data.DeriveLiftedInstances.Internal: [cst] :: Derivator -> Q Exp -> Q Exp
+ Data.DeriveLiftedInstances.Internal: [eff] :: Derivator -> Q Exp -> Q Exp
+ Data.DeriveLiftedInstances.Internal: [inp] :: Derivator -> Q Pat -> Q Pat
+ Data.DeriveLiftedInstances.Internal: className :: Type -> Name
- Data.DeriveLiftedInstances: Derivator :: (Q Exp -> Q Exp) -> (Name -> Q Exp -> Q Exp) -> (Type -> Q Exp -> Q Exp) -> ((Q Exp -> Q Exp -> Q Exp) -> Q Exp -> Q Exp) -> (Q Exp -> Q Exp -> Q Exp) -> Derivator
+ Data.DeriveLiftedInstances: Derivator :: (Q Exp -> Q Exp) -> (Q Exp -> Q Exp) -> (Q Exp -> Q Exp) -> (Name -> Q Exp -> Q Exp) -> (Type -> Q Exp -> Q Exp) -> ((Q Exp -> Q Exp -> Q Exp) -> Q Exp -> Q Exp) -> (Q Pat -> Q Pat) -> (Q Exp -> Q Exp -> Q Exp) -> Derivator
- Data.DeriveLiftedInstances.Internal: Derivator :: (Q Exp -> Q Exp) -> (Name -> Q Exp -> Q Exp) -> (Type -> Q Exp -> Q Exp) -> ((Q Exp -> Q Exp -> Q Exp) -> Q Exp -> Q Exp) -> (Q Exp -> Q Exp -> Q Exp) -> Derivator
+ Data.DeriveLiftedInstances.Internal: Derivator :: (Q Exp -> Q Exp) -> (Q Exp -> Q Exp) -> (Q Exp -> Q Exp) -> (Name -> Q Exp -> Q Exp) -> (Type -> Q Exp -> Q Exp) -> ((Q Exp -> Q Exp -> Q Exp) -> Q Exp -> Q Exp) -> (Q Pat -> Q Pat) -> (Q Exp -> Q Exp -> Q Exp) -> Derivator

Files

CHANGELOG.md view
@@ -1,6 +1,11 @@ Changelog ========= +v0.2   2020-06-29+-----------------+  - Support deriving co- and dialgebraic classes with `cst` and `eff`+  - Support doing pattern matching for input variables with `inp`+ v0.1.1 2020-06-24 -----------------   - Add `monoidDerivBy`
Data/DeriveLiftedInstances.hs view
@@ -14,10 +14,7 @@ module Data.DeriveLiftedInstances (   -- * Deriving instances   deriveInstance,-  -- * Derivators for any class   idDeriv, newtypeDeriv, isoDeriv,-  -- * Derivators for algebraic classes-  -- $algebraic-classes   recordDeriv, apDeriv, biapDeriv, monoidDeriv, monoidDerivBy,   showDeriv, ShowsPrec(..),   -- * Creating derivators@@ -26,14 +23,14 @@  import Language.Haskell.TH import Data.DeriveLiftedInstances.Internal+ import Control.Applicative (liftA2)-import Control.Monad (zipWithM)+ import Data.Biapplicative-import Data.Reflection+import Data.Bifoldable --- $algebraic-classes--- Algebraic classes are type classes where all the methods return a value of the same type, which is also the class parameter.--- Examples from base are `Num` and `Monoid`.+import Control.Monad (zipWithM)+import Data.Reflection  -- | Given how to derive an instance for @a@, `apDeriv` creates a `Derivator` for @f a@, -- when @f@ is an instance of `Applicative`. Example:@@ -46,11 +43,14 @@ -- @ apDeriv :: Derivator -> Derivator apDeriv deriv = Derivator {-  res = \v -> [| fmap (\w -> $(res deriv [| w |])) $v |],+  res = \e -> [| fmap (\w -> $(res deriv [| w |])) $e |],+  cst = \e -> [| foldMap (\w -> $(cst deriv [| w |])) $e |],+  eff = \e -> [| traverse (\w -> $(eff deriv [| w |])) $e |],   op  = \nm o -> [| pure $(op deriv nm o) |],   arg = \ty e -> [| pure $(arg deriv ty e) |],   var = \fold v ->     [| fmap (\w -> $(var deriv fold [| w |])) ($(fold [| traverse |] [| id |]) $v) |],+  inp = id,   ap  = \f a -> [| liftA2 (\g b -> $(ap deriv [| g |] [| b |])) $f $a |] } @@ -66,11 +66,14 @@ biapDeriv :: Derivator -> Derivator -> Derivator biapDeriv l r = Derivator {   res = \e -> [| bimap (\w -> $(res l [| w |])) (\w -> $(res r [| w |])) $e |],+  cst = \e -> [| bifoldMap (\w -> $(cst l [| w |])) (\w -> $(cst r [| w |])) $e |],+  eff = \e -> [| bitraverse (\w -> $(eff l [| w |])) (\w -> $(eff r [| w |])) $e |],   op  = \nm o -> [| bipure $(op l nm o) $(op r nm o) |],   arg = \ty e -> [| bipure $(arg l ty e) $(arg r ty e) |],   var = \fold v ->     [| bimap (\w -> $(var l fold [| w |])) (\w -> $(var r fold [| w |]))        ($(fold [| traverseBia |] [| id |]) $v) |],+  inp = id,   ap  = \f a -> [| biliftA2 (\g b -> $(ap l [| g |] [| b |])) (\g b -> $(ap r [| g |] [| b |])) $f $a |] } @@ -79,8 +82,13 @@ monoidDeriv :: Derivator monoidDeriv = monoidDerivBy [| (<>) |] [| mempty |] +-- | Create a `Derivator` for a monoid, given TH expressions to replace `(<>)` and `mempty` respectively. Example:+--+-- @monoidDerivBy [| (+) |] [| 0 |]@ monoidDerivBy :: Q Exp -> Q Exp -> Derivator monoidDerivBy append empty = idDeriv {+  cst = const [| mempty |],+  eff = \e -> [| pure $e |],   op  = \_ _ -> empty,   arg = \_ _ -> empty,   var = \fold v -> [| ($(fold [| foldMapBy $append $empty |] [| id |]) $v) |],@@ -141,10 +149,13 @@ -- @ recordDeriv :: Q Exp -> [(Q Exp, Derivator)] -> Derivator recordDeriv mk flds = Derivator {-  res = \vs -> do vnms <- vars; [| case $vs of $(pat vnms) -> $(exps vnms >>= foldl (\f v -> [| $f $(pure v) |]) mk) |],+  res = \vs -> do vnms <- vars; [| case $vs of $(pat vnms) -> $(foldl (\f ((_, d), v) -> [| $f $(res d (ex v)) |]) mk (zip flds vnms)) |],+  cst = \vs -> do vnms <- vars; [| case $vs of $(pat vnms) -> $(foldl (\f ((_, d), v) -> [| $f <> $(cst d (ex v)) |]) [| mempty |] (zip flds vnms)) |],+  eff = \vs -> do vnms <- vars; [| case $vs of $(pat vnms) -> $(foldl (\f ((_, d), v) -> [| $f <*> $(eff d (ex v)) |]) [| pure $mk |] (zip flds vnms)) |],   op  = \nm o -> tup $ traverse (\(_, d) -> op d nm o) flds,   arg = \ty e -> tup $ traverse (\(_, d) -> arg d ty e) flds,   var = \fold v -> tup $ traverse (\(fld, d) -> var d fold [| $(fold [| fmap |] fld) $v |]) flds,+  inp = id,   ap  = \fs as -> do     fnms <- funs     vnms <- vars@@ -161,8 +172,6 @@     pat = pure . TupP . fmap VarP     ex :: Name -> Q Exp     ex = pure . VarE-    exps :: [Name] -> Q [Exp]-    exps = traverse ex     vars :: Q [Name]     vars = names "a"     funs :: Q [Name]
Data/DeriveLiftedInstances/Internal.hs view
@@ -30,8 +30,8 @@ -- @ -- meth0 :: a -- meth1 :: Int -> a--- meth2 :: a -> Either Bool a -> Int--- meth3 :: Maybe [a] -> a+-- meth2 :: a -> Either Bool a -> Sum Int+-- meth3 :: Maybe [a] -> IO a -- @ -- -- the resulting template haskell declarations are (pseudo code):@@ -39,36 +39,34 @@ -- @ -- meth0 = $res ($op "meth0" meth0) -- meth1 a = $res (($op "meth1" meth1) `$ap` ($arg Int a))--- meth2 v0 v1 = (($op "meth2" meth2) `$ap` ($var fold0 v0)) `$ap` ($var fold1 v1)---   where---     fold0 _ f = f---     fold1 map f = [| $map $f |]--- meth3 v2 = $res (($op "meth2" meth2) `$ap` ($var fold2 v2))---   where---     fold2 map f = [| $map ($map $f) |]+-- meth2 ($inp v0) ($inp v1) = $cst (($op "meth2" meth2) `$ap` ($var (`iterate` 0) v0)) `$ap` ($var (`iterate` 1) v1)+-- meth3 ($inp v2) = $eff (($op "meth2" meth2) `$ap` ($var (`iterate` 2) v2)) -- @ data Derivator = Derivator {   res :: Q Exp -> Q Exp, -- ^ Convert the result of the method+  cst :: Q Exp -> Q Exp, -- ^ Convert the result of the method if it is a constant+  eff :: Q Exp -> Q Exp, -- ^ Convert the result of the method if it has an effect   op  :: Name -> Q Exp -> Q Exp, -- ^ Convert the method (still unapplied to any arguments)   arg :: Type -> Q Exp -> Q Exp, -- ^ Convert an argument   var :: (Q Exp -> Q Exp -> Q Exp) -> Q Exp -> Q Exp, -- ^ Convert a variable+  inp :: Q Pat -> Q Pat, -- ^ Generate an input pattern for a variable   ap  :: Q Exp -> Q Exp -> Q Exp -- ^ Apply an argument or variable to the method }  varExp :: Name -> Q Exp varExp = pure . VarE -varPat :: Name -> Q Pat-varPat = pure . VarP- -- | The identity `Derivator`. Not useful on its own, but often used as input for other `Derivator`s. idDeriv :: Derivator idDeriv = Derivator {-  res  = id,-  op   = const id,-  arg  = const id,-  var  = const id,-  ap   = \f a -> [| $f $a |]+  res = id,+  cst = id,+  eff = id,+  op  = const id,+  arg = const id,+  var = const id,+  inp = id,+  ap  = \f a -> [| $f $a |] }  -- | Derive the instance with the given `Derivator` and the given instance head.@@ -85,26 +83,27 @@ deriveInstance deriv qtyp = do   typ <- qtyp   case typ of-    ForallT _ ctx (AppT (ConT className) typeName) ->-      deriveInstance' deriv ctx className typeName-    AppT (ConT className) typeName ->-      deriveInstance' deriv [] className typeName+    ForallT _ ctx (AppT ty typeName) ->+      deriveInstance' deriv ctx (className ty) (AppT ty typeName)+    AppT ty typeName ->+      deriveInstance' deriv [] (className ty) (AppT ty typeName)     _ -> fail $ "No support for type: " ++ show typ  deriveInstance' :: Derivator -> Cxt -> Name -> Type -> Q [Dec]-deriveInstance' deriv ctx className typeName = do-  ClassI (ClassD _ _ [KindedTV tvn _] _ decs) _ <- reify className+deriveInstance' deriv ctx clsName typ = do+  ClassI (ClassD _ _ tvs _ decs) _ <- reify clsName+  let KindedTV tvn _ = last tvs   impl <- for decs $ \case     SigD nm tp -> do       dec <- reify nm       case dec of         ClassOpI{} -> do           (argNames, body) <- buildOperation deriv tvn tp (op deriv nm (varExp nm))-          let args = map (\argName -> if contains argName body then VarP argName else WildP) argNames+          args <- traverse (\argName -> if contains argName body then (if nameBase argName == "var" then inp deriv else id) . pure . VarP $ argName else pure WildP) argNames           pure $ Just $ FunD nm [Clause args (NormalB body) []]         _ -> fail $ "No support for declaration: " ++ show dec     _ -> pure Nothing-  pure [InstanceD Nothing ctx (AppT (ConT className) typeName) $ catMaybes impl]+  pure [InstanceD Nothing ctx typ $ catMaybes impl]  buildOperation :: Derivator -> Name -> Type -> Q Exp -> Q ([Name], Exp) buildOperation d nm (AppT (AppT ArrowT h) t) e | hasVar nm h = do@@ -116,8 +115,9 @@   (args, rhs) <- buildOperation d nm t (ap d e (arg d h (varExp varNm)))   pure (varNm : args, rhs) buildOperation d nm (ForallT _ _ t) e = buildOperation d nm t e+buildOperation d nm (AppT _ t) e | isVar nm t = ([],) <$> eff d e buildOperation d nm t e | isVar nm t = ([],) <$> res d e-                        | otherwise = ([],) <$> e+                        | otherwise = ([],) <$> cst d e  buildArgument :: Name -> Type -> Q Exp -> Q Exp -> Q Exp buildArgument nm (AppT h _) _ var | isVar nm h = var@@ -134,9 +134,10 @@ hasVar nm (AppT f a) = isVar nm f || hasVar nm a hasVar _ _ = False -tvName :: TyVarBndr -> Name-tvName (PlainTV nm) = nm-tvName (KindedTV nm _) = nm+className :: Type -> Name+className (ConT nm) = nm+className (AppT h _) = className h+className t = error $ "No support for class: " ++ show t  contains :: Data d => Name -> d -> Bool contains nm = gmapQl (||) False (\d -> maybe (contains nm d) (== nm) $ cast d)
README.md view
@@ -3,3 +3,32 @@  [![Hackage](https://img.shields.io/hackage/v/derive-lifted-instances.svg)](https://hackage.haskell.org/package/derive-lifted-instances) [![Build Status](https://travis-ci.org/sjoerdvisscher/derive-lifted-instances.svg?branch=master)](https://travis-ci.org/github/sjoerdvisscher/derive-lifted-instances)++`derive-lifted-instances` generates type class instances using Template Haskell.++Below is an overview of what this library can do. If you could rewrite a class as one of the cases, and the listed constraints are satisfiable, then an instance can be derived. Note that when another instance of the class is required, this could also be a derived instance+(i.e. deriving is composable), in case you don't want that instance to actually exist.++|              | `class C x where alg :: f x -> x`+|--------------|---+| `x` iso `y`  | `(Functor f, C y)`+| `x=m`        | `(Foldable f, Monoid m)`+| `x=t a`      | `(Traversable f, Applicative t, C a)`+| `x=t a b`    | `(Traversable f, Biapplicative t, C a, C b)`+| `x` a record | `(Traversable f, All C flds)`++|              | `class C x where coalg :: x -> f x`+|--------------|---+| `x` iso `y`  | `(Functor f, C y)`+| `x=m`        | `(Pointed f)`+| `x=t a`      | `(Applicative f, Traversable t, C a)`+| `x=t a b`    | `(Applicative f, Bitraversable t, C a, C b)`+| `x` a record | `(Applicative f, All C flds)`++|              | `class C x where dialg :: f x -> g x`+|--------------|---+| `x` iso `y`  | `(Functor f, Functor g, C y)`+| `x=m`        | `(Foldable f, Porinted g, Monoid m)`+| `x=t a`      | `(Traversable f, Applicative g, Applicative t, Traversable t, C a)`+| `x=t a b`    | `(Traversable f, Applicative g, Biapplicative t, Bitraversable t, C a, C b)`+| `x` a record | `(Traversable f, Applicative g, All C flds)`
derive-lifted-instances.cabal view
@@ -1,5 +1,5 @@ name:                derive-lifted-instances-version:             0.1.1+version:             0.2 synopsis:            Derive class instances though various kinds of lifting description:         Helper functions to use Template Haskell for generating class instances. homepage:            https://github.com/sjoerdvisscher/derive-lifted-instances
examples/Test.hs view
@@ -20,13 +20,6 @@   op1 i is = i + sum is   op2 = sum . fmap sum -data Rec f = Rec { getUnit :: f (), getInt :: f Int }-deriving instance (Show (f ()), Show (f Int)) => Show (Rec f)-deriveInstance (recordDeriv [| Rec |]-    [ ([| getUnit |], apDeriv monoidDeriv)-    , ([| getInt |], apDeriv idDeriv)-    ]) [t| forall f. Applicative f => Test (Rec f) |]- newtype X = X { unX :: Int } deriving Show mkX :: Int -> X mkX = X . (`mod` 10)@@ -34,6 +27,15 @@ deriveInstance (isoDeriv [| mkX |] [| unX |] idDeriv) [t| Test X |] deriveInstance (isoDeriv [| mkX |] [| unX |] idDeriv) [t| Eq X |] deriveInstance (isoDeriv [| mkX |] [| unX |] idDeriv) [t| Ord X |]++data Rec f = Rec { getUnit :: f (), getInt :: f Int, getX :: f X }+deriving instance (Show (f ()), Show (f Int), Show (f X)) => Show (Rec f)+deriving instance (Eq (f ()), Eq (f Int), Eq (f X)) => Eq (Rec f)+-- deriveInstance (recordDeriv [| Rec |]+--     [ ([| getUnit |], apDeriv monoidDeriv)+--     , ([| getInt |], apDeriv idDeriv)+--     , ([| getX |], apDeriv (isoDeriv [| mkX |] [| unX |] idDeriv))+--     ]) [t| forall f. (Applicative f, Foldable f, Ord (f ()), Ord (f Int), Ord (f X)) => Ord (Rec f) |]  deriveInstance showDeriv [t| Test ShowsPrec |] deriveInstance monoidDeriv [t| Test () |]