packages feed

htree-0.1.1.0: src/Data/HTree/List.hs

{-# OPTIONS_GHC -Wno-unrecognised-pragmas #-}

{-# HLINT ignore "Eta reduce" #-}

-- | implements a heterogeneous list to use for forests of heterogeneous trees
module Data.HTree.List
  ( -- * heterogeneous list
    HList ((:::), HSing, ..)

    -- * mapping
  , hcmap
  , hmap

    -- * traversing
  , htraverse
  , hctraverse

    -- * folding
  , hcFold

    -- * helpers
  , allTopHList
  , hconcat
  )
where

import Data.Functor.Identity (Identity (Identity, runIdentity))
import Data.HTree.Constraint (withDict, pattern Dict, type Dict)
import Data.HTree.Families (All, Top, type (++))
import Data.Kind (Type)

-- | A heterogeneous list
--
-- >>> "bla" `HCons` 23 `HCons` HNil :: HList Identity '[ String, Int ]
-- HCons (Identity "bla") (HCons (Identity 23) HNil)
type HList :: forall k. (k -> Type) -> [k] -> Type
data HList f ts where
  HCons :: forall f x xs. f x -> HList f xs -> HList f (x : xs)
  HNil :: forall f. HList f '[]

-- | pattern synonym for 'HCons'
--
-- >>> t = "bla" ::: 23 ::: HNil :: HList Identity '[ String, Int ]
-- >>> t
-- HCons (Identity "bla") (HCons (Identity 23) HNil)
-- >>> case t of (x ::: _) -> runIdentity x
-- "bla"
pattern (:::) :: forall f x xs. f x -> HList f xs -> HList f (x : xs)
pattern x ::: xs = HCons x xs

-- | pattern that allows to construct a singleton HList
--
-- >>> HSing 42 :: HList Identity '[ Int ]
-- HCons (Identity 42) HNil
pattern HSing :: forall f a. f a -> HList f '[a]
pattern HSing x = HCons x HNil

-- | map with a function that maps forall f a
hmap :: forall f g xs. (forall a. f a -> g a) -> HList f xs -> HList g xs
hmap f l = withDict (allTopHList l) $ hcmap @Top @f @g f l

-- | map with a constraint that holds for all elements of the  list
--
-- >>> import Data.Functor.Const
-- >>> hcmap @Show (Const . show . runIdentity) (42 `HCons` HSing "bla" :: HList Identity '[ Int, String ])
-- HCons (Const "42") (HCons (Const "\"bla\"") HNil)
hcmap :: forall c f g xs. All c xs => (forall a. c a => f a -> g a) -> HList f xs -> HList g xs
hcmap f = runIdentity . hctraverse @c @Identity @f @g (Identity . f)

-- | traverse a structure with a function
htraverse :: forall t f g xs. Applicative t => (forall a. f a -> t (g a)) -> HList f xs -> t (HList g xs)
htraverse f l = withDict (allTopHList l) $ hctraverse @Top @t @f @g f l

-- | traverse a structure such that a constraint holds; this is the workhorse of mapping and traversing
--
-- >>> import Data.Functor.Const
-- >>> hctraverse @Show (Just . Const . show . runIdentity) (42 `HCons` HSing "bla" :: HList Identity '[ Int, String ])
-- Just (HCons (Const "42") (HCons (Const "\"bla\"") HNil))
hctraverse :: forall c t f g xs. (All c xs, Applicative t) => (forall a. c a => f a -> t (g a)) -> HList f xs -> t (HList g xs)
hctraverse _ HNil = pure HNil
hctraverse f (HCons x xs) = HCons <$> f x <*> hctraverse @c @t @f @g f xs

-- | foldr for HLists.
hcFold :: forall c f b xs. All c xs => (forall a. c a => f a -> b -> b) -> b -> HList f xs -> b
hcFold _ def HNil = def
hcFold f def (x `HCons` xs) = f x $ hcFold @c f def xs

-- | witnesses that for all HLists, we can always derive the All Top constraint
allTopHList :: forall f xs. HList f xs -> Dict (All Top xs)
allTopHList HNil = Dict
allTopHList (HCons _ xs) = case allTopHList xs of
  Dict -> Dict

-- | concats two heterogeneous lists
hconcat :: forall f xs ys. HList f xs -> HList f ys -> HList f (xs ++ ys)
hconcat HNil ys = ys
hconcat (x `HCons` xs) ys = x `HCons` xs `hconcat` ys

infixr 5 `HCons`

infixr 5 :::

infixr 5 `hconcat`

deriving stock instance Show (HList f '[])

deriving stock instance (Show (f x), Show (HList f xs)) => Show (HList f (x : xs))

deriving stock instance Eq (HList f '[])

deriving stock instance (Eq (f x), Eq (HList f xs)) => Eq (HList f (x : xs))