lvish-1.1.1.1: Data/LVar/Generic.hs
{-# LANGUAGE Trustworthy #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE DataKinds #-} -- For Determinism
-- | A generic interface providing operations that work on /all/ LVars.
module Data.LVar.Generic
(
-- * Classes containing the generic interfaces
LVarData1(..), LVarWBottom(..),
OrderedLVarData1(..),
-- * Supporting types and utilities
AFoldable(..),
castFrzn, forFrzn
)
where
import Control.LVish.Types
import Control.LVish.Basics
import Control.LVish.Internal (Par, Determinism(..))
import Control.LVish.DeepFrz.Internal (Frzn, Trvrsbl)
import qualified Data.Foldable as F
import Data.List (sort)
import GHC.Prim (unsafeCoerce#)
import System.IO.Unsafe (unsafeDupablePerformIO)
import Data.LVar.Generic.Internal
--------------------------------------------------------------------------------
-- | Some LVar datatypes are stored in an /internally/ ordered way so
-- that it is then possible to take /O(1)/ frozen snapshots and consume them
-- inexpensively in a deterministic order.
--
-- LVars with this additional property provide this class as well as `LVarData1`.
class LVarData1 f => OrderedLVarData1 (f :: * -> * -> *) where
-- | Don't just freeze the LVar, but make the full contents
-- completely available and `Foldable`. Guaranteed /O(1)/.
snapFreeze :: f s a -> Par QuasiDet s (f Trvrsbl a)
{-
-- | Just like LVarData1 but for type constructors of kind `*`.
class LVarData0 (t :: *) where
-- | This associated type models a picture of the "complete" contents of the data:
-- e.g. a whole set instead of one element, or the full/empty information for an
-- IVar, instead of just the payload.
type Snapshot0 t
freeze0 :: t -> Par QuasiDet s (Snapshot0 t)
newBottom0 :: Par d s t
-}
------------------------------------------------------------------------------
-- Dealing with frozen LVars.
------------------------------------------------------------------------------
-- | `Trvrsbl` is a stronger property than `Frzn`, so it is always safe to \"upcast\" to
-- the weaker version.
castFrzn :: LVarData1 f => f Trvrsbl a -> f Frzn a
castFrzn x = unsafeCoerceLVar x
-- | LVish `Par` actions must commute, therefore one safe way to consume a frozen (but
-- unordered) LVar, even in another `runPar` session, is to run a `Par` computation for
-- each element.
forFrzn :: LVarData1 f => f Frzn a -> (a -> Par d s ()) -> Par d s ()
forFrzn fzn fn =
F.foldrM (\ a () -> fn a) () $
unsafeDupablePerformIO $ -- ASSUME idempotence.
unsafeTraversable fzn
-- | For any LVar, we have a generic way to freeze it in a `runParThenFreeze`.
-- instance (DeepFrz a, LVarData1 f) => DeepFrz (f s a) where
-- type FrzType (f s a) = f Frzn a
-- frz = unsafeCoerceLVar
-- ^^^
-- Note that this doesn't work because it CONFLICTS with the other DeepFrz instances.
-- There's no way that we can prove to GHC that pure data will NEVER be an instance
-- of LVarData1, and therefore will never actually cause a conflict with e above
-- instance.