prim-uniq-0.2/0000755000000000000000000000000013645615774011504 5ustar0000000000000000prim-uniq-0.2/Setup.lhs0000644000000000000000000000011613645615774013312 0ustar0000000000000000#!/usr/bin/env runhaskell > import Distribution.Simple > main = defaultMain prim-uniq-0.2/ChangeLog.md0000644000000000000000000000031713645615774013656 0ustar0000000000000000# Revision history for prim-uniq ## 0.2 - 2020-04-11 * Update for dependent-sum 0.7. (Support GHC 8.8.) * `dependent-sum` dropped the `(:=)` type. Use `(:~:)` instead. * Fix a minor strictness issue (#1). prim-uniq-0.2/prim-uniq.cabal0000644000000000000000000000257313645615774014420 0ustar0000000000000000name: prim-uniq version: 0.2 stability: provisional cabal-version: >= 1.6 build-type: Simple author: James Cook maintainer: Obsidian Systems, LLC license: PublicDomain homepage: https://github.com/obsidiansystems/prim-uniq category: Data, Dependent Types synopsis: Opaque unique identifiers in primitive state monads description: Opaque unique identifiers in primitive state monads and a GADT-like type using them as witnesses of type equality. extra-source-files: README.md ChangeLog.md tested-with: GHC == 8.0.2, GHC == 8.2.2, GHC == 8.4.3, GHC == 8.6.3, GHC == 8.8.3 source-repository head type: git location: https://github.com/obsidiansystems/prim-uniq Library hs-source-dirs: src exposed-modules: Data.Unique.Prim Data.Unique.Tag Unsafe.Unique.Prim Unsafe.Unique.Tag build-depends: base >= 3 && <5, dependent-sum >= 0.7 && < 0.8, primitive prim-uniq-0.2/README.md0000644000000000000000000000301213645615774012757 0ustar0000000000000000[![Build Status](https://travis-ci.org/github/obsidiansystems/prim-uniq.svg)](https://travis-ci.org/obsidiansystems/prim-uniq) Unique values and an ad-hoc "unique-tag GADT" ============================================= This library defines 2 types - `Uniq` and `Tag`. `Uniq` is a traditional "unique value" type, extended with a state-token type parameter so it works in both `IO` and `ST`. `Tag` is like `Uniq` with the addition of a phantom type parameter. The type of that parameter is fixed at the time the `Tag` is created, so the uniqueness of the tag means that equality of tag values witnesses equality of their phantom types. In other words, given two `Tag`s, if they are equal then their phantom type parameters are the same - just like pattern matching on a GADT constructor. The `GEq` and `GCompare` classes from the `dependent-sum` package can be used to discover that type equality, allowing `Tag` to be used for a pretty cool semi-dynamic typing scheme. For example (using the `dependent-map` library): import Data.Unique.Tag import Data.Dependent.Map main = do x <- newTag y <- newTag z <- newTag let m1 = fromList [x :=> 3, y :=> "hello"] m2 = fromList [x :=> 17, z :=> (True, x)] -- the type checker would (rightly) reject this line: -- m3 = singleton y ("foo", "bar") print (m1 ! x) print (m1 ! y) print (m2 ! x) print (m1 ! snd (m2 ! z)) Which would print: 3 "hello" 17 3 prim-uniq-0.2/src/0000755000000000000000000000000013645615774012273 5ustar0000000000000000prim-uniq-0.2/src/Unsafe/0000755000000000000000000000000013645615774013514 5ustar0000000000000000prim-uniq-0.2/src/Unsafe/Unique/0000755000000000000000000000000013645615774014762 5ustar0000000000000000prim-uniq-0.2/src/Unsafe/Unique/Prim.hs0000644000000000000000000000642013645615774016227 0ustar0000000000000000{-# LANGUAGE BangPatterns, FlexibleInstances #-} module Unsafe.Unique.Prim ( Uniq, getUniq , unsafeMkUniq, unsafeShowsPrecUniq, unsafeShowUniq ) where import Control.Monad.Primitive import Data.IORef import System.IO.Unsafe -- A smaller numeric type could be used, such as Word or Word64, but I -- want to be able to guarantee uniqueness even over very long execution -- times. Smaller types would require either checking for overflow, -- accepting the possibility of aliasing, or tracking allocation and -- deallocation, which would be a lot of extra work. Word64 is almost -- certainly big enough for practical purposes, though. Allocating one -- 'Uniq' every nanosecond, it would take 584 years to start aliasing.... -- So, in the future I may choose to switch to Word64. -- |A 'Uniq' is a value that can only be constructed under controlled -- conditions (in IO or ST, basically), and once constructed can only be -- compared to 'Uniq' values created under the same conditions (in the same -- monad). Upon comparison, a 'Uniq' is ONLY ever equal to itself. Beyond -- that, no promises regarding ordering are made except that once constructed -- the order is deterministic and a proper ordering relation (eg, > is -- transitive and irreflexive, etc.) newtype Uniq s = Uniq Integer deriving (Eq, Ord) -- |There is only one 'RealWorld', so this instance is sound (unlike the -- general 'unsafeShowsPrecUniq'). Note that there is no particular -- relationship between 'Uniq' values (or the strings 'show' turns them into) -- created in different executions of a program. The value they render to -- should be considered completely arbitrary, and the Show instance only even -- exists for convenience when testing code that uses 'Uniq's. instance Show (Uniq RealWorld) where showsPrec = unsafeShowsPrecUniq {-# NOINLINE nextUniq #-} -- | [internal] Assuming the compiler behaves "as expected", this is a single -- statically-created IORef holding the counter which will be used as the -- source of new 'Prim' keys (in 'ST' and 'IO'). nextUniq :: IORef Integer nextUniq = unsafePerformIO (newIORef 0) -- |Construct a new 'Uniq' that is equal to itself, unequal to every other -- 'Uniq' constructed in the same monad, and incomparable to every 'Uniq' -- constructed in any other monad. getUniq :: PrimMonad m => m (Uniq (PrimState m)) getUniq = unsafePrimToPrim (atomicModifyIORef' nextUniq (\u -> (u+1, Uniq u))) -- |For the implementation of 'Uniq' construction in new monads, this operation -- is exposed. Users must accept responsibility for ensuring true uniqueness -- across the lifetime of the resulting 'Uniq' value. Failure to do so could -- lead to type unsoundness in code depending on uniqueness as a type witness -- (eg, "Data.Unique.Tag"). unsafeMkUniq :: Integer -> Uniq s unsafeMkUniq n = Uniq n -- |A `Show` instance for @`Uniq` s@ would not be sound, but for debugging -- purposes we occasionally will want to do it anyway. Its unsoundness is -- nicely demonstrated by: -- -- > runST (fmap show getUniq) :: String -- -- Which, despite having type 'String', is not referentially transparent. unsafeShowsPrecUniq :: Int -> Uniq s -> ShowS unsafeShowsPrecUniq p (Uniq u) = showsPrec p u -- |See 'unsafeShowsPrecUniq'. unsafeShowUniq :: Uniq s -> String unsafeShowUniq (Uniq u) = show u prim-uniq-0.2/src/Unsafe/Unique/Tag.hs0000644000000000000000000000507513645615774016040 0ustar0000000000000000{-# 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 import Data.Type.Equality ((:~:)(..)) -- |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 prim-uniq-0.2/src/Data/0000755000000000000000000000000013645615774013144 5ustar0000000000000000prim-uniq-0.2/src/Data/Unique/0000755000000000000000000000000013645615774014412 5ustar0000000000000000prim-uniq-0.2/src/Data/Unique/Prim.hs0000644000000000000000000000024513645615774015656 0ustar0000000000000000{-# LANGUAGE Trustworthy #-} module Data.Unique.Prim ( Uniq, getUniq, RealWorld ) where import Unsafe.Unique.Prim import Control.Monad.Primitive (RealWorld)prim-uniq-0.2/src/Data/Unique/Tag.hs0000644000000000000000000000047713645615774015471 0ustar0000000000000000{-# LANGUAGE Trustworthy #-} module Data.Unique.Tag ( Tag , newTag , RealWorld , (:~:)(..) , GEq(..) , GOrdering(..) , GCompare(..) ) where import Data.GADT.Compare import Unsafe.Unique.Tag import Data.Type.Equality ((:~:)(..)) import Control.Monad.Primitive (RealWorld)