linear-base-0.1.0: src/Data/Unrestricted/Internal/Dupable.hs
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE LinearTypes #-}
{-# LANGUAGE GADTs #-}
module Data.Unrestricted.Internal.Dupable
(
-- * Dupable
Dupable(..)
, dup
, dup3
) where
import Data.Unrestricted.Internal.Consumable
import GHC.TypeLits
import Data.Type.Equality
import Data.V.Linear.Internal.V (V)
import qualified Data.V.Linear.Internal.V as V
-- | The laws of @Dupable@ are dual to those of 'Monoid':
--
-- * @first consume (dup2 a) ≃ a ≃ second consume (dup2 a)@ (neutrality)
-- * @first dup2 (dup2 a) ≃ (second dup2 (dup2 a))@ (associativity)
--
-- Where the @(≃)@ sign represents equality up to type isomorphism.
--
-- When implementing 'Dupable' instances for composite types, using 'dupV'
-- should be more convenient since 'V' has a zipping 'Applicative' instance.
class Consumable a => Dupable a where
{-# MINIMAL dupV | dup2 #-}
dupV :: forall n. KnownNat n => a %1-> V n a
dupV a =
case V.caseNat @n of
Prelude.Left Refl -> a `lseq` V.make @0 @a
Prelude.Right Refl -> V.iterate dup2 a
dup2 :: a %1-> (a, a)
dup2 a = V.elim (dupV @a @2 a) (,)
dup3 :: Dupable a => a %1-> (a, a, a)
dup3 x = V.elim (dupV @_ @3 x) (,,)
dup :: Dupable a => a %1-> (a, a)
dup = dup2