{-# LANGUAGE GADTs, FlexibleInstances #-} module Unsafe.Unique.Tag ( Tag , newTag , veryUnsafeMkTag ) where import Data.GADT.Compare import Data.GADT.Show import Unsafe.Unique.Prim import Unsafe.Coerce import Control.Monad.Primitive import Control.Monad -- |The 'Tag' type is like an ad-hoc GADT allowing runtime creation of new -- constructors. Specifically, it is like a GADT \"enumeration\" with one -- phantom type. -- -- A 'Tag' constructor can be generated in any primitive monad (but only tags -- from the same one can be compared). Every tag is equal to itself and to -- no other. The 'GOrdering' class allows rediscovery of a tag's phantom type, -- so that 'Tag's and values of type @'DSum' ('Tag' s)@ can be tested for -- equality even when their types are not known to be equal. -- -- 'Tag' uses a 'Uniq' as a witness of type equality, which is sound as long -- as the 'Uniq' is truly unique and only one 'Tag' is ever constructed from -- any given 'Uniq'. The type of 'newTag' enforces these conditions. -- 'veryUnsafeMkTag' provides a way for adventurous (or malicious!) users to -- assert that they know better than the type system. newtype Tag s a = Tag (Uniq s) deriving (Eq, Ord) instance Show (Tag RealWorld a) where showsPrec p (Tag u) = showsPrec p u instance GShow (Tag RealWorld) where gshowsPrec = showsPrec instance GEq (Tag s) where geq (Tag a) (Tag b) | a == b = Just (unsafeCoerce Refl) | otherwise = Nothing instance GCompare (Tag s) where gcompare (Tag a) (Tag b) = case compare a b of LT -> GLT EQ -> unsafeCoerce (GEQ :: GOrdering () ()) GT -> GGT -- |Create a new tag witnessing a type @a@. The 'GEq' or 'GOrdering' instance -- can be used to discover type equality of two occurrences of the same tag. -- -- (I'm not sure whether the recovery is sound if @a@ is instantiated as a -- polymorphic type, so I'd advise caution if you intend to try it. I suspect -- it is, but I have not thought through it very deeply and certainly have not -- proved it.) newTag :: PrimMonad m => m (Tag (PrimState m) a) newTag = liftM Tag getUniq -- |Very dangerous! This is essentially a deferred 'unsafeCoerce': by creating -- a tag with this function, the user accepts responsibility for ensuring -- uniqueness of the 'Integer' across the lifetime of the 'Tag' (including -- properly controlling the lifetime of the 'Tag' if necessary -- by universal quantification when discharging the @s@ phantom type) veryUnsafeMkTag :: Integer -> Tag s a veryUnsafeMkTag = Tag . unsafeMkUniq