heftia-0.1.0.0: src/Data/Hefty/Extensible.hs
{-# LANGUAGE UndecidableInstances #-}
-- This Source Code Form is subject to the terms of the Mozilla Public
-- License, v. 2.0. If a copy of the MPL was not distributed with this
-- file, You can obtain one at https://mozilla.org/MPL/2.0/.
{- |
Copyright : (c) 2023 Yamada Ryo
License : MPL-2.0 (see the file LICENSE)
Maintainer : ymdfield@outlook.jp
Stability : experimental
Portability : portable
An implementation of an open union for higher-order effects using
the [extensible](https://hackage.haskell.org/package/extensible) package as a backend.
-}
module Data.Hefty.Extensible where
import Control.Effect.Class (Signature)
import Control.Effect.Class.Machinery.HFunctor (HFunctor, hfmap)
import Data.Extensible (Forall, Match (Match), htabulateFor, leadership, match)
import Data.Extensible.Sum (exhaust, strikeAt, (<:|), type (:/) (EmbedAt))
import Data.Free.Extensible (TypeIndex, findFirstMembership)
import Data.Hefty.Union (
UnionH (
HasMembershipH,
absurdUnionH,
inject0H,
injectH,
projectH,
weakenH,
(|+:)
),
)
import Data.Proxy (Proxy (Proxy))
import GHC.TypeNats (KnownNat)
import Type.Membership (nextMembership)
{- |
An implementation of an open union for higher-order effects using
the [extensible](https://hackage.haskell.org/package/extensible) package as a backend.
-}
newtype ExtensibleUnionH hs f a = ExtensibleUnionH {unExtensibleUnionH :: hs :/ FieldAppH f a}
newtype FieldAppH f a (h :: Signature) = FieldAppH {unFieldAppH :: h f a}
instance Forall HFunctor hs => HFunctor (ExtensibleUnionH hs) where
hfmap f =
ExtensibleUnionH
. match
( htabulateFor @HFunctor Proxy \w ->
Match $ EmbedAt w . FieldAppH . hfmap f . unFieldAppH
)
. unExtensibleUnionH
{-# INLINE hfmap #-}
-- todo: Functor, Foldable, Traversable instances
instance UnionH ExtensibleUnionH where
type HasMembershipH _ h hs = KnownNat (TypeIndex hs h)
injectH = ExtensibleUnionH . EmbedAt findFirstMembership . FieldAppH
{-# INLINE injectH #-}
projectH (ExtensibleUnionH u) = unFieldAppH <$> strikeAt findFirstMembership u
{-# INLINE projectH #-}
absurdUnionH = exhaust . unExtensibleUnionH
{-# INLINE absurdUnionH #-}
inject0H = ExtensibleUnionH . EmbedAt leadership . FieldAppH
{-# INLINE inject0H #-}
weakenH (ExtensibleUnionH (EmbedAt w e)) =
ExtensibleUnionH $ EmbedAt (nextMembership w) e
{-# INLINE weakenH #-}
f |+: g = (f . unFieldAppH <:| g . ExtensibleUnionH) . unExtensibleUnionH
{-# INLINE (|+:) #-}