{-# LANGUAGE ViewPatterns #-} {-# LANGUAGE TemplateHaskell #-} {-# OPTIONS_GHC -fno-warn-orphans #-} ----------------------------------------------------------------------------- -- | -- Module : Data.DeriveLiftedInstances -- Copyright : (c) Sjoerd Visscher 2020 -- -- Maintainer : sjoerd@w3future.com -- Stability : experimental -- Portability : non-portable ----------------------------------------------------------------------------- module Data.DeriveLiftedInstances ( -- * Deriving instances deriveInstance, -- * Derivators for any class idDeriv, newtypeDeriv, isoDeriv, -- * Derivators for algebraic classes -- $algebraic-classes apDeriv, tupleDeriv, unitDeriv, showDeriv, ShowsPrec(..), -- * Creating derivators Derivator(..) ) where import Language.Haskell.TH import Data.DeriveLiftedInstances.Internal import Control.Arrow ((***)) import Control.Applicative (liftA2) -- $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`. -- | Given how to derive an instance for @a@, `apDeriv` creates a `Derivator` for @f a@, -- when @f@ is an instance of `Applicative`. Example: -- -- @ -- `deriveInstance` (`apDeriv` `idDeriv`) [t| forall a. `Num` a => `Num` [a] |] -- -- > [2, 3] `*` [5, 10] -- [10, 20, 15, 30] -- @ apDeriv :: Derivator -> Derivator apDeriv deriv = deriv { res = \v -> [| fmap (\w -> $(res deriv [| w |])) $v |], 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) |], ap = \f a -> [| liftA2 (\g b -> $(ap deriv [| g |] [| b |])) $f $a |] } -- | Given how to derive an instance for @a@ and @b@, `tupleDeriv` creates a `Derivator` for @(a, b)@. -- -- @ -- `deriveInstance` (`tupleDeriv` `idDeriv` `idDeriv`) [t| forall a b. (`Num` a, `Num` b) => `Num` (a, b) |] -- -- > (2, 3) `*` (5, 10) -- (10, 30) -- @ tupleDeriv :: Derivator -> Derivator -> Derivator tupleDeriv l r = idDeriv { res = \e -> [| ((\w -> $(res l [| w |])) *** (\w -> $(res r [| w |]))) $e |], op = \nm o -> [| ($(op l nm o), $(op r nm o)) |], arg = \ty e -> [| ($(arg l ty e), $(arg r ty e)) |], var = \fold v -> [| ( $(var l fold [| $(fold [| fmap |] [| fst |]) $v |]) , $(var r fold [| $(fold [| fmap |] [| snd |]) $v |]) ) |], ap = \f a -> [| case ($f, $a) of ((g, h), (b, c)) -> ($(ap l [| g |] [| b |]), $(ap r [| h |] [| c |])) |] } -- | A `Derivator` for @()@. unitDeriv :: Derivator unitDeriv = idDeriv { op = \_ _ -> [| () |], ap = const } -- | Given how to derive an instance for @a@, and the names of a newtype wrapper around @a@, -- `newtypeDeriv` creates a `Derivator` for that newtype. Example: -- -- @ -- newtype Ap f a = Ap { getAp :: f a } deriving Show -- `deriveInstance` (`newtypeDeriv` 'Ap 'getAp `idDeriv`) [t| forall f. `Functor` f => `Functor` (Ap f) |] -- -- > `fmap` (+1) (Ap [1,2,3]) -- Ap {getAp = [2,3,4]} -- @ newtypeDeriv :: Name -> Name -> Derivator -> Derivator newtypeDeriv mk un = isoDeriv (pure $ ConE mk) (pure $ VarE un) -- | Given how to derive an instance for @a@, and two functions of type @a `->` b@ and @b `->` a@, -- `isoDeriv` creates a `Derivator` for @b@. (Note that the 2 functions don't have to form -- an isomorphism, but if they don't, the new instance can break the class laws.) Example: -- -- @ -- newtype X = X { unX :: `Int` } deriving `Show` -- mkX :: `Int` -> X -- mkX i = X (`mod` i 10) -- `deriveInstance` (isoDeriv [| mkX |] [| unX |] `idDeriv`) [t| `Num` X |] -- -- > mkX 4 `^` 2 -- X {unX = 6} -- @ isoDeriv :: Q Exp -> Q Exp -> Derivator -> Derivator isoDeriv mk un deriv = deriv { res = \v -> [| $mk $(res deriv v) |], var = \fold v -> var deriv fold [| $(fold [| fmap |] un) $v |] } deriveInstance showDeriv [t| Bounded ShowsPrec |] deriveInstance showDeriv [t| Num ShowsPrec |] deriveInstance showDeriv [t| Fractional ShowsPrec |] deriveInstance showDeriv [t| Floating ShowsPrec |] deriveInstance showDeriv [t| Semigroup ShowsPrec |] deriveInstance showDeriv [t| Monoid ShowsPrec |]