heftia-0.1.0.0: src/Data/Free/Union.hs
-- 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
A type class representing a general open union for first-order effects, independent of the internal
implementation.
-}
module Data.Free.Union where
import Control.Effect.Class (Instruction, type (~>))
import Data.Kind (Constraint)
{- |
A type class representing a general open union for first-order effects, independent of the internal
implementation.
-}
class Union (u :: [Instruction] -> Instruction) where
{-# MINIMAL inject, project, absurdUnion, (comp | (inject0, weaken), decomp | (|+|:)) #-}
type HasMembership u (f :: Instruction) (fs :: [Instruction]) :: Constraint
inject :: HasMembership u f fs => f ~> u fs
project :: HasMembership u f fs => u fs a -> Maybe (f a)
absurdUnion :: u '[] a -> x
comp :: Either (f a) (u fs a) -> u (f ': fs) a
comp = \case
Left x -> inject0 x
Right x -> weaken x
{-# INLINE comp #-}
decomp :: u (f ': fs) a -> Either (f a) (u fs a)
decomp = Left |+|: Right
{-# INLINE decomp #-}
infixr 5 |+|:
(|+|:) :: (f a -> r) -> (u fs a -> r) -> u (f ': fs) a -> r
(f |+|: g) u = case decomp u of
Left x -> f x
Right x -> g x
{-# INLINE (|+|:) #-}
inject0 :: f ~> u (f ': fs)
inject0 = comp . Left
{-# INLINE inject0 #-}
injectUnder :: f2 ~> u (f1 ': f2 ': fs)
injectUnder = weaken . inject0
{-# INLINE injectUnder #-}
injectUnder2 :: f3 ~> u (f1 ': f2 ': f3 ': fs)
injectUnder2 = weaken2 . inject0
{-# INLINE injectUnder2 #-}
injectUnder3 :: f4 ~> u (f1 ': f2 ': f3 ': f4 ': fs)
injectUnder3 = weaken3 . inject0
{-# INLINE injectUnder3 #-}
weaken :: u fs a -> u (f ': fs) a
weaken = comp . Right
{-# INLINE weaken #-}
weaken2 :: u fs a -> u (f1 ': f2 ': fs) a
weaken2 = weaken . weaken
{-# INLINE weaken2 #-}
weaken3 :: u fs a -> u (f1 ': f2 ': f3 ': fs) a
weaken3 = weaken2 . weaken
{-# INLINE weaken3 #-}
weaken4 :: u fs a -> u (f1 ': f2 ': f3 ': f4 ': fs) a
weaken4 = weaken3 . weaken
{-# INLINE weaken4 #-}
weakenUnder :: u (f1 ': fs) ~> u (f1 ': f2 ': fs)
weakenUnder = inject0 |+|: weaken2
weakenUnder2 :: u (f1 ': f2 ': fs) ~> u (f1 ': f2 ': f3 ': fs)
weakenUnder2 = inject0 |+|: injectUnder |+|: weaken3
weakenUnder3 :: u (f1 ': f2 ': f3 ': fs) ~> u (f1 ': f2 ': f3 ': f4 ': fs)
weakenUnder3 = inject0 |+|: injectUnder |+|: injectUnder2 |+|: weaken4
weaken2Under :: u (f1 ': fs) ~> u (f1 ': f2 ': f3 ': fs)
weaken2Under = inject0 |+|: weaken3
weaken2Under2 :: u (f1 ': f2 ': fs) ~> u (f1 ': f2 ': f3 ': f4 ': fs)
weaken2Under2 = inject0 |+|: injectUnder |+|: weaken4
weaken3Under :: u (f1 ': fs) ~> u (f1 ': f2 ': f3 ': f4 ': fs)
weaken3Under = inject0 |+|: weaken4
flipUnion :: u (f1 ': f2 ': fs) ~> u (f2 ': f1 ': fs)
flipUnion = injectUnder |+|: inject0 |+|: weaken2
flipUnion3 :: u (f1 ': f2 ': f3 ': fs) ~> u (f3 ': f2 ': f1 ': fs)
flipUnion3 = injectUnder2 |+|: injectUnder |+|: inject0 |+|: weaken3
flipUnionUnder :: u (f1 ': f2 ': f3 ': fs) ~> u (f1 ': f3 ': f2 ': fs)
flipUnionUnder = inject0 |+|: injectUnder2 |+|: injectUnder |+|: weaken3
rot3 :: u (f1 ': f2 ': f3 ': fs) ~> u (f2 ': f3 ': f1 ': fs)
rot3 = injectUnder2 |+|: inject0 |+|: injectUnder |+|: weaken3
rot3' :: u (f1 ': f2 ': f3 ': fs) ~> u (f3 ': f1 ': f2 ': fs)
rot3' = injectUnder |+|: injectUnder2 |+|: inject0 |+|: weaken3
bundleUnion2 :: Union u' => u (f1 ': f2 ': fs) ~> u (u' '[f1, f2] ': fs)
bundleUnion2 = inject0 . inject0 |+|: inject0 . injectUnder |+|: weaken
bundleUnion3 :: Union u' => u (f1 ': f2 ': f3 ': fs) ~> u (u' '[f1, f2, f3] ': fs)
bundleUnion3 =
(inject0 . inject0)
|+|: (inject0 . injectUnder)
|+|: (inject0 . injectUnder2)
|+|: weaken
bundleUnion4 :: Union u' => u (f1 ': f2 ': f3 ': f4 ': fs) ~> u (u' '[f1, f2, f3, f4] ': fs)
bundleUnion4 =
(inject0 . inject0)
|+|: (inject0 . injectUnder)
|+|: (inject0 . injectUnder2)
|+|: (inject0 . injectUnder3)
|+|: weaken
unbundleUnion2 :: Union u' => u (u' '[f1, f2] ': fs) ~> u (f1 ': f2 ': fs)
unbundleUnion2 = (inject0 |+|: injectUnder |+|: absurdUnion) |+|: weaken2
unbundleUnion3 :: Union u' => u (u' '[f1, f2, f3] ': fs) ~> u (f1 ': f2 ': f3 ': fs)
unbundleUnion3 = (inject0 |+|: injectUnder |+|: injectUnder2 |+|: absurdUnion) |+|: weaken3
unbundleUnion4 :: Union u' => u (u' '[f1, f2, f3, f4] ': fs) ~> u (f1 ': f2 ': f3 ': f4 ': fs)
unbundleUnion4 =
(inject0 |+|: injectUnder |+|: injectUnder2 |+|: injectUnder3 |+|: absurdUnion)
|+|: weaken4
type family IsMember (f :: Instruction) fs where
IsMember f (f ': fs) = 'True
IsMember f (_ ': fs) = IsMember f fs
IsMember _ '[] = 'False
type Member u f fs = (HasMembership u f fs, IsMember f fs ~ 'True)