{-# OPTIONS_HADDOCK not-home #-}
{-# LANGUAGE ImpredicativeTypes, UnicodeSyntax, LinearTypes, QualifiedDo,
   NoImplicitPrelude, BlockArguments, DefaultSignatures, QuantifiedConstraints,
   UndecidableInstances, AllowAmbiguousTypes #-}
module Data.Linear.Alias.Internal where

import GHC.Generics

import Prelude.Linear
import qualified Control.Concurrent.Counter as Counter

-- Usage: type RefC = RefC' MonadYou'reUsing

-- | A reference counted alias
data Alias m a where
  Alias :: (a  m ())       -- ^ Function to free resource when the last alias is forgotten
        -> !Counter.Counter -- ^ The counter associated to this reference counted alias
        -> a                -- ^ The aliased resource
          Alias m a

-- I don't see a way to instance Functor if the free resource function is kept
-- in the body of the reference (it shows up in both the co- and contra-variant
-- position)

-- Generic utilities

-- | Return all reference counted (recursively nested) fields of @a@.
-- These are not only @'Alias'es@ directly, but all the recursively nested
-- @'Alias'es@ in @a@.
countedFields :: (Generic a, Fields (Rep a)) => a -> [SomeAlias]
-- The alternative of using the 'Data' constraint instead of Generic isn't
-- good, since it would require making @Alias m a@ instance Data, which is
-- hard.
countedFields :: forall a. (Generic a, Fields (Rep a)) => a -> [SomeAlias]
countedFields a
x = Rep a Any -> [SomeAlias]
forall a. Rep a a -> [SomeAlias]
forall {k} (rep :: k -> *) (a :: k).
Fields rep =>
rep a -> [SomeAlias]
fields (a -> Rep a Any
forall x. a -> Rep a x
forall a x. Generic a => a -> Rep a x
from a
x)
{-# INLINE countedFields #-}

data SomeAlias =  m b. SomeAlias (Alias m b)

class Fields rep where
  fields :: rep a -> [SomeAlias]

instance Fields V1 where
  fields :: forall (a :: k). V1 a -> [SomeAlias]
fields V1 a
_ = []

instance Fields U1 where
  fields :: forall (a :: k). U1 a -> [SomeAlias]
fields U1 a
_ = []

instance Fields f => Fields (M1 i c f) where
  fields :: forall (a :: k). M1 i c f a -> [SomeAlias]
fields (M1 f a
x) = f a -> [SomeAlias]
forall (a :: k). f a -> [SomeAlias]
forall {k} (rep :: k -> *) (a :: k).
Fields rep =>
rep a -> [SomeAlias]
fields f a
x

instance (Fields a, Fields b) => Fields (a :+: b) where
  fields :: forall (a :: k). (:+:) a b a -> [SomeAlias]
fields (L1 a a
x) = a a -> [SomeAlias]
forall (a :: k). a a -> [SomeAlias]
forall {k} (rep :: k -> *) (a :: k).
Fields rep =>
rep a -> [SomeAlias]
fields a a
x
  fields (R1 b a
x) = b a -> [SomeAlias]
forall (a :: k). b a -> [SomeAlias]
forall {k} (rep :: k -> *) (a :: k).
Fields rep =>
rep a -> [SomeAlias]
fields b a
x

instance (Fields a, Fields b) => Fields (a :*: b) where
  fields :: forall (a :: k). (:*:) a b a -> [SomeAlias]
fields (a a
a :*: b a
b) = a a -> [SomeAlias]
forall (a :: k). a a -> [SomeAlias]
forall {k} (rep :: k -> *) (a :: k).
Fields rep =>
rep a -> [SomeAlias]
fields a a
a [SomeAlias] %1 -> [SomeAlias] %1 -> [SomeAlias]
forall a. [a] %1 -> [a] %1 -> [a]
++ b a -> [SomeAlias]
forall (a :: k). b a -> [SomeAlias]
forall {k} (rep :: k -> *) (a :: k).
Fields rep =>
rep a -> [SomeAlias]
fields b a
b

-- In the case of an alias, we add it the list of aliased values.
instance Fields (K1 i (Alias m a)) where
  fields :: forall (a :: k). K1 i (Alias m a) a -> [SomeAlias]
fields (K1 Alias m a
r) = [Alias m a -> SomeAlias
forall (m :: * -> *) b. Alias m b -> SomeAlias
SomeAlias Alias m a
r]