git-remote-ipfs-0.1.0.0: src/Network/IPFS/Git/RemoteHelper/Generic.hs
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE MonoLocalBinds #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TypeFamilies #-}
module Network.IPFS.Git.RemoteHelper.Generic
( HKD
, gvalidate
, ginvalidate
)
where
import Generics.SOP
-- The venerable defunctionalisation, applied generically, with a hipster touch
-- as popularised by Sandy "type fam" Maguire ("Higher Kinded Data", HKD)
-- | Eliminates the boring identity functor 'I'
type family HKD (f :: * -> *) (a :: *) :: * where
HKD I a = a
HKD f a = f a
-- | Generically lift the applicative functor 'f' out of a structure.
--
-- Example:
--
-- Given a datatype parametrised over the functor 'Maybe', return 'Just' the
-- value if all 'Maybe' fields are 'Just', thereby changing the functor to 'I'.
-- 'Nothing' otherwise.
--
-- >>> :{
-- >>> data Person f = Person { name :: f String, age :: f Int }
-- >>> deriving (Show, Generic, GHC.Generic)
-- >>> :}
-- >>> gvalidate $ Person (Just "LeBoeuf") (Just 42)
-- Just (Person (I "LeBoeuf") (I 42))
-- >>> gvalidate $ Person Nothing (Just 69)
-- Nothing
--
gvalidate
:: ( Generic (a f)
, Generic (a I)
, Applicative f
, AllZip2 (LiftedCoercible I f) (Code (a f)) (Code (a I))
)
=> a f
-> f (a I)
gvalidate = (to <$>) . hsequence . hcoerce . from
-- | Generically change the functor of a structure from 'I' to 'f', given a
-- constructor of 'f'.
--
-- Morally the inverse of 'gvalidate', although the specialisation to the
-- identity functor makes this trivial.
--
-- If 'a f' is a 'Semigroup', this can be used to apply defaults to a partial
-- value.
--
-- >>> :{
-- >>> instance Semigroup (Person Last) where
-- >>> a <> b = Person { name = on (<>) name a b, age = on (<>) age a b }
-- >>> -- For convenience, also define 'Monoid'
-- >>> instance Monoid (Person Last) were
-- >>> mempty = Person mempty mempty
-- >>> mappend = (<>)
-- >>> :}
-- >>> defaultPerson :: Person I
-- >>> defaultPerson = Person "Sandy" 28
-- >>> gvalidate $ ginvalidate pure defaultPerson <> Person (pure "Maguire") mempty
-- Just (Person (I "Maguire") (I 28)
--
ginvalidate
:: ( Generic (a f)
, Generic (a I)
, AllZip2 (LiftedCoercible f I) (Code (a I)) (Code (a f))
)
=> (forall b. b -> f b)
-> a I
-> a f
ginvalidate f = to . hcoerce . hmap (f . unI) . from