packages feed

mmzk-typeid 0.5.0.1 → 0.5.0.2

raw patch · 22 files changed

+216/−209 lines, 22 files

Files

CHANGELOG.md view
@@ -1,7 +1,15 @@ # Revision history for mmzk-typeid  -## 0.5.0.1 --2023-9-18+## 0.5.0.2 -- 2024-3-10++* Add `Typeable` and `Data` instances for `TypeID` and `KindID`.+  * Kindly contributed by @shinzui.++* Fix all warnings.+++## 0.5.0.1 -- 2023-9-18  * Fix bad links in the documentation. 
README.md view
@@ -22,7 +22,7 @@  1. Generating TypeIDs in a batch. They are guaranteed to have the same timestamp (up to the first 32768 ids) and of ascending order; 2. Encoding the prefix in the [type level](https://hackage.haskell.org/package/mmzk-typeid/docs/Data-KindID.html), so that if you accidentally pass in a wrong prefix, the code won't compile, avoiding the need for runtime checks.-3. Support TypeID with other UUID versions. Currently v7 (default) and v4 are supported.+3. Support TypeID with other UUID versions. Currently v7 (default), v1,  v4, and v5 are supported.  ## Quick start 
mmzk-typeid.cabal view
@@ -1,6 +1,6 @@ cabal-version:      2.4 name:               mmzk-typeid-version:            0.5.0.1+version:            0.5.0.2  synopsis:           A TypeID implementation for Haskell description:@@ -79,6 +79,9 @@         BlockArguments         ConstraintKinds         DataKinds+        DeriveAnyClass+        DeriveDataTypeable+        DeriveGeneric         FlexibleContexts         FlexibleInstances         InstanceSigs@@ -103,6 +106,8 @@         time >=1.11 && <1.13,         uuid ^>=1.3,         uuid-types ^>=1.0,+    ghc-options:+        -Wall     hs-source-dirs:   src     default-language: Haskell2010 @@ -142,6 +147,9 @@         BlockArguments         ConstraintKinds         DataKinds+        DeriveAnyClass+        DeriveDataTypeable+        DeriveGeneric         FlexibleContexts         FlexibleInstances         InstanceSigs@@ -168,6 +176,8 @@         time,         uuid,         uuid-types,+    ghc-options:+        -Wall     hs-source-dirs:         src         test
src/Data/KindID.hs view
@@ -7,10 +7,11 @@ -- Similar to "Data.TypeID", but the type is statically determined in the type -- level. ----- When using 'TypeID', if we want to check if the type matches, we usually need--- to get the prefix of the 'TypeID' and compare it with the desired prefix at--- runtime. However, with Haskell's type system, we can do this at compile time--- instead. We call this TypeID with compile-time prefix a 'KindID'.+-- When using 'Data.TypeID.V7.TypeID', if we want to check if the type matches,+-- we usually need to get the prefix of the 'Data.TypeID.V7.TypeID' and compare+-- it with the desired prefix at runtime. However, with Haskell's type system,+-- we can do this at compile time instead. We call this TypeID with compile-time+-- prefix a 'KindID'. -- -- Of course, that would require the desired prefix to be known at compile time. -- This is actually quite common, especially when we are using one prefix for@@ -87,5 +88,3 @@  import           Data.KindID.Internal (KindID') import           Data.KindID.V7-import           Data.TypeID.Class-import           Data.TypeID.V7 (TypeID)
src/Data/KindID/Class.hs view
@@ -15,11 +15,11 @@     ValidPrefix   , ToPrefix(..)   -- * Helpers-  , LengthSymbol(..)-  , IsLowerSymbol(..)-  , IsLowerChar(..)-  , LSUH(..)-  , ILSUH(..)+  , LengthSymbol+  , IsLowerSymbol+  , IsLowerChar+  , LSUH+  , ILSUH   ) where  import           Data.Type.Bool
src/Data/KindID/Internal.hs view
@@ -13,11 +13,13 @@ import           Data.Aeson.Types hiding (String) import           Data.Binary import           Data.ByteString.Lazy (ByteString)+import           Data.Data (Data) import           Data.Hashable import           Data.Proxy import           Data.KindID.Class import           Data.Text (Text) import qualified Data.Text as T+import           Data.Typeable (Typeable) import           Data.TypeID.Class import           Data.TypeID.Error import           Data.TypeID.Internal (TypeID'(..))@@ -31,14 +33,13 @@ import           Data.UUID.Versions import           Foreign import           GHC.TypeLits (symbolVal)-import           System.Random  -- | A TypeID with the prefix encoded at type level. -- -- It is dubbed 'Data.KindID.V7.KindID' because the prefix here is a data kind -- rather than a type. newtype KindID' (version :: UUIDVersion) prefix = KindID' UUID-  deriving (Eq, Ord)+  deriving (Eq, Ord, Data, Typeable)  instance (ToPrefix prefix, ValidPrefix (PrefixSymbol prefix))   => Show (KindID' version prefix) where@@ -51,9 +52,9 @@     readsPrec :: Int -> String -> [(KindID' version prefix, String)]     readsPrec _ str = case TID.parseStringS str of       Left _           -> []-      Right (tid, rem) -> case fromTypeID tid of+      Right (tid, nks) -> case fromTypeID tid of         Nothing  -> []-        Just kid -> [(kid, rem)]+        Just kid -> [(kid, nks)]     {-# INLINE readsPrec #-}  instance (ToPrefix prefix, ValidPrefix (PrefixSymbol prefix))
src/Data/KindID/Unsafe.hs view
@@ -4,17 +4,18 @@ -- Maintainer  : mmzk1526@outlook.com -- Portability : GHC ----- Unsafe 'KindID' functions.+-- Unsafe 'Data.KindID.V7.KindID' functions. -- -- It is a re-export of "Data.TypeID.V7.Unsafe". -- module Data.KindID.Unsafe   (-  -- * Unsafe 'KindID' decoding ('KindID'-specific)+  -- * Unsafe 'Data.KindID.V7.KindID' decoding+  -- ('Data.KindID.V7.KindID'-specific)     unsafeParseString   , unsafeParseText   , unsafeParseByteString-  -- * Unsafe 'KindID' decoding (class methods)+  -- * Unsafe 'Data.KindID.V7.KindID' decoding (class methods)   , unsafeString2ID   , unsafeText2ID   , unsafeByteString2ID@@ -22,6 +23,4 @@   , unsafeFromTypeID   ) where -import           Data.KindID.V7 (KindID) import           Data.KindID.V7.Unsafe-import           Data.TypeID.Class
src/Data/KindID/V1.hs view
@@ -51,13 +51,11 @@ import           Control.Monad.IO.Class import           Data.ByteString.Lazy (ByteString) import           Data.KindID.Class-import           Data.KindID.Internal (KindID'(..)) import qualified Data.KindID.Internal as KID import           Data.Text (Text) import           Data.TypeID.Class import           Data.TypeID.Error import           Data.TypeID.V1 (TypeIDV1)-import           Data.TypeID.V7 (TypeID) import           Data.UUID.Types.Internal (UUID) import           Data.UUID.Versions 
src/Data/KindID/V1/Unsafe.hs view
@@ -22,10 +22,8 @@  import           Data.ByteString.Lazy (ByteString) import           Data.KindID.Class-import           Data.KindID.Internal (KindID') import qualified Data.KindID.Internal as KID import           Data.KindID.V1 (KindIDV1)-import           Data.TypeID.Internal (TypeID') import           Data.TypeID.V1 (TypeIDV1) import           Data.Text (Text) import           Data.TypeID.Class
src/Data/KindID/V4.hs view
@@ -53,13 +53,11 @@ import           Control.Monad.IO.Class import           Data.ByteString.Lazy (ByteString) import           Data.KindID.Class-import           Data.KindID.Internal (KindID'(..)) import qualified Data.KindID.Internal as KID import           Data.Text (Text) import           Data.TypeID.Class import           Data.TypeID.Error import           Data.TypeID.V4 (TypeIDV4)-import           Data.TypeID.V7 (TypeID) import           Data.UUID.Types.Internal (UUID) import           Data.UUID.Versions 
src/Data/KindID/V4/Unsafe.hs view
@@ -22,10 +22,8 @@  import           Data.ByteString.Lazy (ByteString) import           Data.KindID.Class-import           Data.KindID.Internal (KindID') import qualified Data.KindID.Internal as KID import           Data.KindID.V4 (KindIDV4)-import           Data.TypeID.Internal (TypeID') import           Data.TypeID.V4 (TypeIDV4) import           Data.Text (Text) import           Data.TypeID.Class
src/Data/KindID/V5.hs view
@@ -51,13 +51,11 @@ import           Control.Monad.IO.Class import           Data.ByteString.Lazy (ByteString) import           Data.KindID.Class-import           Data.KindID.Internal (KindID'(..)) import qualified Data.KindID.Internal as KID import           Data.Text (Text) import           Data.TypeID.Class import           Data.TypeID.Error import           Data.TypeID.V5 (TypeIDV5)-import           Data.TypeID.V7 (TypeID) import           Data.UUID.Types.Internal (UUID) import           Data.UUID.Versions import           Data.Word
src/Data/KindID/V5/Unsafe.hs view
@@ -22,10 +22,8 @@  import           Data.ByteString.Lazy (ByteString) import           Data.KindID.Class-import           Data.KindID.Internal (KindID') import qualified Data.KindID.Internal as KID import           Data.KindID.V5 (KindIDV5)-import           Data.TypeID.Internal (TypeID') import           Data.TypeID.V5 (TypeIDV5) import           Data.Text (Text) import           Data.TypeID.Class
src/Data/KindID/V7/Unsafe.hs view
@@ -22,10 +22,8 @@  import           Data.ByteString.Lazy (ByteString) import           Data.KindID.Class-import           Data.KindID.Internal (KindID') import qualified Data.KindID.Internal as KID import           Data.KindID.V7 (KindID)-import           Data.TypeID.Internal (TypeID') import           Data.TypeID.V7 (TypeID) import           Data.Text (Text) import           Data.TypeID.Class@@ -60,9 +58,10 @@ unsafeParseByteString = KID.unsafeParseByteString {-# INLINE unsafeParseByteString #-} --- | Convert a 'TypeID'' to a 'KindID''. If the actual prefix does not match--- with the expected one as defined by the type, it does not complain and--- produces a wrong 'KindID''.+-- | Convert a 'Data.TypeID.Internal.TypeID'' to a+-- 'Data.KindID.Internal.KindID''. If the actual prefix does not match with the+-- expected one as defined by the type, it does not complain and produces a+-- wrong 'Data.KindID.Internal.KindID''. unsafeFromTypeID :: (ToPrefix prefix, ValidPrefix (PrefixSymbol prefix))                  => TypeID -> KindID prefix unsafeFromTypeID = KID.unsafeFromTypeID
src/Data/TypeID/Class.hs view
@@ -26,8 +26,8 @@   , checkID   , checkIDWithEnv   -- * Helper types-  , GenFunc(..)-  , ResWithErr(..)+  , GenFunc+  , ResWithErr   ) where  import           Control.Exception
src/Data/TypeID/Internal.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE UndecidableInstances #-} -- | -- Module      : Data.TypeID.Internal -- License     : MIT@@ -23,16 +24,17 @@ import           Data.ByteString.Lazy (ByteString) import qualified Data.ByteString.Lazy as BSL import           Data.Char+import           Data.Data (Data) import           Data.Hashable import           Data.Proxy import           Data.String import           Data.Text (Text) import qualified Data.Text as T import           Data.Text.Encoding+import           Data.Typeable (Typeable) import           Data.TypeID.Class import           Data.TypeID.Error import           Data.UUID.Types.Internal (UUID(..))-import qualified Data.UUID.Types.Internal as UUID import qualified Data.UUID.V1 as V1 import qualified Data.UUID.V4 as V4 import qualified Data.UUID.V5 as V5@@ -47,7 +49,7 @@ --  The constructor is not exposed to the public API to prevent generating -- invalid 'TypeID''s. data TypeID' (version :: UUIDVersion) = TypeID' Text UUID-  deriving (Eq, Ord)+  deriving (Eq, Ord, Data, Typeable)  instance Show (TypeID' version) where   show :: TypeID' version -> String@@ -472,15 +474,15 @@ parseStringS str = case span (/= '_') str of   ("", _)              -> Left TypeIDExtraSeparator   (_, "")              -> do-    let (uuid, rem) = splitAt 26 str+    let (uuid, nks) = splitAt 26 str         bs          = fromString uuid-    (, rem) . TypeID' "" <$> decodeUUID bs+    (, nks) . TypeID' "" <$> decodeUUID bs   (prefix, _ : suffix) -> do     let prefix'     = T.pack prefix-        (uuid, rem) = splitAt 26 suffix+        (uuid, nks) = splitAt 26 suffix         bs          = fromString uuid     case checkPrefix prefix' of-      Nothing  -> (, rem) . TypeID' prefix' <$> decodeUUID bs+      Nothing  -> (, nks) . TypeID' prefix' <$> decodeUUID bs       Just err -> Left err  -- | Parse a 'TypeID'' from its string representation as a strict 'Text'. It is
src/Data/TypeID/Unsafe.hs view
@@ -4,17 +4,17 @@ -- Maintainer  : mmzk1526@outlook.com -- Portability : GHC ----- Unsafe 'TypeID' functions.+-- Unsafe 'Data.TypeID.V7.TypeID' functions. -- -- It is a re-export of "Data.TypeID.V7.Unsafe". -- module Data.TypeID.Unsafe   (-  -- * Unsafe 'TypeID' generation+  -- * Unsafe 'Data.TypeID.V7.TypeID' generation     unsafeGenTypeID   , unsafeGenTypeID'   , unsafeGenTypeIDs-  -- * Unsafe decoding ('TypeID'-specific)+  -- * Unsafe decoding ('Data.TypeID.V7.TypeID'-specific)   , unsafeParseString   , unsafeParseText   , unsafeParseByteString@@ -25,5 +25,4 @@   ) where  import           Data.TypeID.Class-import           Data.TypeID.V7 (TypeID) import           Data.TypeID.V7.Unsafe
src/Data/TypeID/V1/Unsafe.hs view
@@ -26,7 +26,6 @@ import           Data.TypeID.Class import qualified Data.TypeID.Internal as TID import           Data.TypeID.V1 (TypeIDV1)-import           Data.UUID.Types.Internal (UUID)  -- | Generate a new 'TypeIDV1' from a prefix, but without checking if the prefix -- is valid.
src/Data/TypeID/V4/Unsafe.hs view
@@ -27,7 +27,6 @@ import           Data.TypeID.Class import qualified Data.TypeID.Internal as TID import           Data.TypeID.V4 (TypeIDV4)-import           Data.UUID.Types.Internal (UUID)  -- | Generate a new 'TypeIDV4' from a prefix, but without checking if the prefix -- is valid.@@ -35,7 +34,8 @@ unsafeGenTypeID = TID.unsafeGenTypeIDV4 {-# INLINE unsafeGenTypeID #-} --- | Generate a new 'TypeIDV4' from a prefix based on insecure 'UUID'v4.+-- | Generate a new 'TypeIDV4' from a prefix based on insecure+-- 'Data.UUID.Types.Internal.UUID'v4. unsafeGenTypeID' :: MonadIO m => Text -> m TypeIDV4 unsafeGenTypeID' = TID.unsafeGenTypeIDV4' {-# INLINE unsafeGenTypeID' #-}
src/Data/TypeID/V5/Unsafe.hs view
@@ -20,7 +20,6 @@   , unsafeByteString2ID   ) where -import           Control.Monad.IO.Class import           Data.ByteString.Lazy (ByteString) import           Data.Text (Text) import           Data.TypeID.Class
src/Data/TypeID/V7/Unsafe.hs view
@@ -28,7 +28,6 @@ import           Data.TypeID.Class import qualified Data.TypeID.Internal as TID import           Data.TypeID.V7 (TypeID)-import           Data.UUID.Types.Internal (UUID) import           Data.Word  -- | Generate a new 'TypeID' from a prefix, but without checking if the prefix@@ -37,8 +36,9 @@ unsafeGenTypeID = TID.unsafeGenTypeID {-# INLINE unsafeGenTypeID #-} --- | Generate a new 'TypeID' from a prefix based on stateless 'UUID'v7, but--- without checking if the prefix is valid.+-- | Generate a new 'TypeID' from a prefix based on stateless +-- 'Data.UUID.Types.Internal.UUID'v7, but without checking if the prefix is+-- valid. unsafeGenTypeID' :: MonadIO m => Text -> m TypeID unsafeGenTypeID' = TID.unsafeGenTypeID' {-# INLINE unsafeGenTypeID' #-}@@ -47,7 +47,8 @@ -- valid. -- -- It tries its best to generate 'TypeID's at the same timestamp, but it may not--- be possible if we are asking too many 'UUID's at the same time.+-- be possible if we are asking too many 'Data.UUID.Types.Internal.UUID's at the+-- same time. -- -- It is guaranteed that the first 32768 'TypeID's are generated at the same -- timestamp.
test/Spec.hs view
@@ -1,7 +1,7 @@-{-# LANGUAGE DeriveAnyClass #-}-{-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE DuplicateRecordFields #-} +{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}+ import           Control.Monad import           Data.Aeson import           Data.Binary (get, put)@@ -17,7 +17,6 @@ import qualified Data.Map as M import           Data.String import           Data.Text (Text)-import qualified Data.Text as T import           Data.TypeID import           Data.TypeID.V1 (TypeIDV1) import           Data.TypeID.V4 (TypeIDV4)@@ -125,9 +124,9 @@                           , ("long", "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz")                           , ("ascii", "château") ]     describe "can detect invalid prefix" do-      forM_ invalidPrefixes \(reason, prefix) -> it reason do-        genID @TypeID prefix `shouldThrow` anyTypeIDError-        case decorate @TypeID prefix nil of+      forM_ invalidPrefixes \(reason, pref) -> it reason do+        genID @TypeID pref `shouldThrow` anyTypeIDError+        case decorate @TypeID pref nil of           Left _  -> pure ()           Right _ -> expectationFailure "Should not be able to decorate with invalid prefix"     let invalidSuffixes = [ ("spaces", " ")@@ -150,10 +149,10 @@                         , ("ten", "0000000000000000000000000a", "00000000-0000-0000-0000-00000000000a")                         , ("sixteen", "0000000000000000000000000g", "00000000-0000-0000-0000-000000000010")                         , ("thirty-two", "00000000000000000000000010", "00000000-0000-0000-0000-000000000020") ]-    forM_ specialValues \(reason, tid, uuid) -> it reason do+    forM_ specialValues \(reason, tid, uid) -> it reason do       case string2ID @TypeID tid of-        Left err  -> expectationFailure $ "Parse error: " ++ show err-        Right tid -> show (getUUID tid) `shouldBe` uuid+        Left err -> expectationFailure $ "Parse error: " ++ show err+        Right t  -> show (getUUID t) `shouldBe` uid    describe "TypeID valid JSON instances" do     it "Decode and then encode should be identity" do@@ -164,51 +163,51 @@       decode json `shouldBe` Just mapping       fmap encode (decode @(Map TypeID TypeID) json) `shouldBe` Just json     describe "Valid JSON value" do-      forM_ valid \(TestData name tid (Just prefix) (Just uuid)) -> it name do+      forM_ valid \(TestData n tid (Just pref) (Just uid)) -> it n do         case decode @TypeID (fromString $ show tid) of-          Nothing  -> expectationFailure "Parse JSON failed!"-          Just tid -> do-            getPrefix tid `shouldBe` prefix-            show (getUUID tid) `shouldBe` uuid+          Nothing -> expectationFailure "Parse JSON failed!"+          Just t  -> do+            getPrefix t `shouldBe` pref+            show (getUUID t) `shouldBe` uid     describe "Valid JSON key" do-      forM_ valid \(TestData name tid (Just prefix) (Just uuid)) -> it name do+      forM_ valid \(TestData n tid (Just pref) (Just uid)) -> it n do         case decode @(Map TypeID Int) (fromString $ "{" ++ show tid ++ ":" ++ "114514" ++ "}") of           Nothing      -> expectationFailure "Parse JSON failed!"           Just mapping -> do-            let (tid, _) = M.elemAt 0 mapping-            getPrefix tid `shouldBe` prefix-            show (getUUID tid) `shouldBe` uuid+            let (t, _) = M.elemAt 0 mapping+            getPrefix t `shouldBe` pref+            show (getUUID t) `shouldBe` uid    describe "TypeID invalid JSON instances" do     describe "Invalid JSON value" do-      forM_ invalid \(TestData name tid _ _) -> it name do+      forM_ invalid \(TestData n tid _ _) -> it n do         case decode @TypeID (fromString $ show tid) of-          Nothing  -> pure ()-          Just tid -> expectationFailure $ "Parsed TypeID: " ++ show tid+          Nothing -> pure ()+          Just t  -> expectationFailure $ "Parsed TypeID: " ++ show t     describe "Invalid JSON key" do-      forM_ invalid \(TestData name tid _ _) -> it name do+      forM_ invalid \(TestData n tid _ _) -> it n do         case decode @(Map TypeID Int) (fromString $ "{" ++ show tid ++ ":" ++ "114514" ++ "}") of-          Nothing  -> pure ()-          Just tid -> expectationFailure "Invalid TypeID key shouldn't be parsed!"+          Nothing -> pure ()+          _       -> expectationFailure "Invalid TypeID key shouldn't be parsed!"    describe "Test invalid.json" do-    forM_ invalid \(TestData name tid _ _) -> it name do+    forM_ invalid \(TestData n tid _ _) -> it n do       case string2ID @TypeID tid of-        Left _    -> pure ()-        Right tid -> expectationFailure $ "Parsed TypeID: " ++ show tid+        Left _  -> pure ()+        Right t -> expectationFailure $ "Parsed TypeID: " ++ show t    describe "Test valid.json (TypeID as literal)" do-    forM_ valid \(TestData name tid (Just prefix) (Just uuid)) -> it name do+    forM_ valid \(TestData n tid (Just pref) (Just uid)) -> it n do       case string2ID @TypeID tid of-        Left err  -> expectationFailure $ "Parse error: " ++ show err-        Right tid -> do-          getPrefix tid `shouldBe` prefix-          show (getUUID tid) `shouldBe` uuid+        Left err -> expectationFailure $ "Parse error: " ++ show err+        Right t  -> do+          getPrefix t `shouldBe` pref+          show (getUUID t) `shouldBe` uid    describe "Test valid.json (TypeID as JSON)" do-    forM_ validUUID \(TestDataUUID name tid prefix uuid) -> it name do-      getPrefix tid `shouldBe` prefix-      getUUID tid `shouldBe` uuid+    forM_ validUUID \(TestDataUUID n tid pref uid) -> it n do+      getPrefix tid `shouldBe` pref+      getUUID tid `shouldBe` uid    describe "Generate KindID with 'Symbol' prefixes" do     it "can generate KindID with prefix" do@@ -240,7 +239,7 @@         Right kid -> getPrefix kid `shouldBe` "mmzk"     it "cannot parse KindID into wrong prefix" do       case string2ID @(KindID "foo") "mmzk_00041061050r3gg28a1c60t3gf" of-        Left err  -> pure ()+        Left _    -> pure ()         Right kid -> expectationFailure $ "Parsed TypeID: " ++ show kid    describe "Generate KindID with custom data kind prefixes" do@@ -253,7 +252,7 @@         Right kid -> getPrefix kid `shouldBe` "user"     it "cannot parse KindID into wrong prefix" do       case string2ID @(KindID 'Comment) "user_00041061050r3gg28a1c60t3gf" of-        Left err  -> pure ()+        Left _    -> pure ()         Right kid -> expectationFailure $ "Parsed KindID: " ++ show kid     it "can generate in batch with same timestamp and in ascending order" do       kids <- withChecks $ genIDs @(KindID 'Comment) 1526@@ -311,7 +310,6 @@       tid <- withCheck $ genID @TypeIDV1 ""       getPrefix tid `shouldBe` ""     it "can generate TypeIDV1 with insecure UUIDv4" do-      start <- V7.getEpochMilli       tid   <- withCheck $ genID' @TypeIDV1 "mmzk"       getPrefix tid `shouldBe` "mmzk"     it "can parse TypeIDV1 from String" do@@ -327,9 +325,9 @@                           , ("long", "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz")                           , ("ascii", "château") ]     describe "can detect invalid prefix" do-      forM_ invalidPrefixes \(reason, prefix) -> it reason do-        genID @TypeIDV1 prefix `shouldThrow` anyTypeIDError-        case decorate @TypeIDV1 prefix nil of+      forM_ invalidPrefixes \(reason, pref) -> it reason do+        genID @TypeIDV1 pref `shouldThrow` anyTypeIDError+        case decorate @TypeIDV1 pref nil of           Left _  -> pure ()           Right _ -> expectationFailure "Should not be able to decorate with invalid prefix"     let invalidSuffixes = [ ("spaces", " ")@@ -352,10 +350,10 @@                         , ("ten", "0000000000000000000000000a", "00000000-0000-0000-0000-00000000000a")                         , ("sixteen", "0000000000000000000000000g", "00000000-0000-0000-0000-000000000010")                         , ("thirty-two", "00000000000000000000000010", "00000000-0000-0000-0000-000000000020") ]-    forM_ specialValues \(reason, tid, uuid) -> it reason do+    forM_ specialValues \(reason, tid, uid) -> it reason do       case string2ID @TypeIDV1 tid of-        Left err  -> expectationFailure $ "Parse error: " ++ show err-        Right tid -> show (getUUID tid) `shouldBe` uuid+        Left err -> expectationFailure $ "Parse error: " ++ show err+        Right t  -> show (getUUID t) `shouldBe` uid    describe "TypeIDV1 valid JSON instances" do     it "Decode and then encode should be identity" do@@ -366,51 +364,51 @@       decode json `shouldBe` Just mapping       fmap encode (decode @(Map TypeIDV1 TypeIDV1) json) `shouldBe` Just json     describe "Valid JSON value" do-      forM_ valid \(TestData name tid (Just prefix) (Just uuid)) -> it name do+      forM_ valid \(TestData n tid (Just pref) (Just uid)) -> it n do         case decode @TypeIDV1 (fromString $ show tid) of           Nothing  -> expectationFailure "Parse JSON failed!"-          Just tid -> do-            getPrefix tid `shouldBe` prefix-            show (getUUID tid) `shouldBe` uuid+          Just t -> do+            getPrefix t `shouldBe` pref+            show (getUUID t) `shouldBe` uid     describe "Valid JSON key" do-      forM_ valid \(TestData name tid (Just prefix) (Just uuid)) -> it name do+      forM_ valid \(TestData n tid (Just pref) (Just uid)) -> it n do         case decode @(Map TypeIDV1 Int) (fromString $ "{" ++ show tid ++ ":" ++ "114514" ++ "}") of           Nothing      -> expectationFailure "Parse JSON failed!"           Just mapping -> do-            let (tid, _) = M.elemAt 0 mapping-            getPrefix tid `shouldBe` prefix-            show (getUUID tid) `shouldBe` uuid+            let (t, _) = M.elemAt 0 mapping+            getPrefix t `shouldBe` pref+            show (getUUID t) `shouldBe` uid    describe "TypeIDV1 invalid JSON instances" do     describe "Invalid JSON value" do-      forM_ invalid \(TestData name tid _ _) -> it name do+      forM_ invalid \(TestData n tid _ _) -> it n do         case decode @TypeIDV1 (fromString $ show tid) of-          Nothing  -> pure ()-          Just tid -> expectationFailure $ "Parsed TypeID: " ++ show tid+          Nothing -> pure ()+          Just t  -> expectationFailure $ "Parsed TypeID: " ++ show t     describe "Invalid JSON key" do-      forM_ invalid \(TestData name tid _ _) -> it name do+      forM_ invalid \(TestData n tid _ _) -> it n do         case decode @(Map TypeIDV1 Int) (fromString $ "{" ++ show tid ++ ":" ++ "114514" ++ "}") of           Nothing  -> pure ()-          Just tid -> expectationFailure "Invalid TypeID key shouldn't be parsed!"+          _        -> expectationFailure "Invalid TypeID key shouldn't be parsed!"    describe "Test invalid.json" do-    forM_ invalid \(TestData name tid _ _) -> it name do+    forM_ invalid \(TestData n tid _ _) -> it n do       case string2ID @TypeIDV1 tid of-        Left _    -> pure ()-        Right tid -> expectationFailure $ "Parsed TypeID: " ++ show tid+        Left _  -> pure ()+        Right t -> expectationFailure $ "Parsed TypeID: " ++ show t    describe "Test valid.json (TypeIDV1 as literal)" do-    forM_ valid \(TestData name tid (Just prefix) (Just uuid)) -> it name do+    forM_ valid \(TestData n tid (Just pref) (Just uid)) -> it n do       case string2ID @TypeIDV1 tid of-        Left err  -> expectationFailure $ "Parse error: " ++ show err-        Right tid -> do-          getPrefix tid `shouldBe` prefix-          show (getUUID tid) `shouldBe` uuid+        Left err -> expectationFailure $ "Parse error: " ++ show err+        Right t  -> do+          getPrefix t `shouldBe` pref+          show (getUUID t) `shouldBe` uid    describe "Test valid.json (TypeIDV1 as JSON)" do-    forM_ validUUID \(TestDataUUID name tid prefix uuid) -> it name do-      getPrefix tid `shouldBe` prefix-      getUUID tid `shouldBe` uuid+    forM_ validUUID \(TestDataUUID n tid pref uid) -> it n do+      getPrefix tid `shouldBe` pref+      getUUID tid `shouldBe` uid    describe "Generate KindIDV1 with 'Symbol' prefixes" do     it "can generate KindIDV1 with prefix" do@@ -425,7 +423,7 @@         Right kid -> getPrefix kid `shouldBe` "mmzk"     it "cannot parse KindID into wrong prefix" do       case string2ID @(KindIDV1 "foo") "mmzk_5hjpeh96458fct8t49fnf9farw" of-        Left err  -> pure ()+        Left _    -> pure ()         Right kid -> expectationFailure $ "Parsed TypeID: " ++ show kid    describe "Generate KindIDV1 with custom data kind prefixes" do@@ -438,7 +436,7 @@         Right kid -> getPrefix kid `shouldBe` "user"     it "cannot parse KindIDV1 into wrong prefix" do       case string2ID @(KindIDV1 'Comment) "user_00041061050r3gg28a1c60t3gf" of-        Left err  -> pure ()+        Left _    -> pure ()         Right kid -> expectationFailure $ "Parsed KindIDV1: " ++ show kid    describe "Binary instance for TypeIDV1 and KindIDV1" do@@ -490,7 +488,6 @@       tid <- withCheck $ genID @TypeIDV4 ""       getPrefix tid `shouldBe` ""     it "can generate TypeIDV4 with insecure UUIDv4" do-      start <- V7.getEpochMilli       tid   <- withCheck $ genID' @TypeIDV4 "mmzk"       getPrefix tid `shouldBe` "mmzk"     it "can parse TypeIDV4 from String" do@@ -506,9 +503,9 @@                           , ("long", "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz")                           , ("ascii", "château") ]     describe "can detect invalid prefix" do-      forM_ invalidPrefixes \(reason, prefix) -> it reason do-        genID @TypeIDV4 prefix `shouldThrow` anyTypeIDError-        case decorate @TypeIDV4 prefix nil of+      forM_ invalidPrefixes \(reason, pref) -> it reason do+        genID @TypeIDV4 pref `shouldThrow` anyTypeIDError+        case decorate @TypeIDV4 pref nil of           Left _  -> pure ()           Right _ -> expectationFailure "Should not be able to decorate with invalid prefix"     let invalidSuffixes = [ ("spaces", " ")@@ -531,10 +528,10 @@                         , ("ten", "0000000000000000000000000a", "00000000-0000-0000-0000-00000000000a")                         , ("sixteen", "0000000000000000000000000g", "00000000-0000-0000-0000-000000000010")                         , ("thirty-two", "00000000000000000000000010", "00000000-0000-0000-0000-000000000020") ]-    forM_ specialValues \(reason, tid, uuid) -> it reason do+    forM_ specialValues \(reason, tid, uid) -> it reason do       case string2ID @TypeIDV4 tid of-        Left err  -> expectationFailure $ "Parse error: " ++ show err-        Right tid -> show (getUUID tid) `shouldBe` uuid+        Left err -> expectationFailure $ "Parse error: " ++ show err+        Right t  -> show (getUUID t) `shouldBe` uid    describe "TypeIDV4 valid JSON instances" do     it "Decode and then encode should be identity" do@@ -545,51 +542,51 @@       decode json `shouldBe` Just mapping       fmap encode (decode @(Map TypeIDV4 TypeIDV4) json) `shouldBe` Just json     describe "Valid JSON value" do-      forM_ valid \(TestData name tid (Just prefix) (Just uuid)) -> it name do+      forM_ valid \(TestData n tid (Just pref) (Just uid)) -> it n do         case decode @TypeIDV4 (fromString $ show tid) of-          Nothing  -> expectationFailure "Parse JSON failed!"-          Just tid -> do-            getPrefix tid `shouldBe` prefix-            show (getUUID tid) `shouldBe` uuid+          Nothing -> expectationFailure "Parse JSON failed!"+          Just t  -> do+            getPrefix t `shouldBe` pref+            show (getUUID t) `shouldBe` uid     describe "Valid JSON key" do-      forM_ valid \(TestData name tid (Just prefix) (Just uuid)) -> it name do+      forM_ valid \(TestData n tid (Just pref) (Just uid)) -> it n do         case decode @(Map TypeIDV4 Int) (fromString $ "{" ++ show tid ++ ":" ++ "114514" ++ "}") of           Nothing      -> expectationFailure "Parse JSON failed!"           Just mapping -> do-            let (tid, _) = M.elemAt 0 mapping-            getPrefix tid `shouldBe` prefix-            show (getUUID tid) `shouldBe` uuid+            let (t, _) = M.elemAt 0 mapping+            getPrefix t `shouldBe` pref+            show (getUUID t) `shouldBe` uid    describe "TypeIDV4 invalid JSON instances" do     describe "Invalid JSON value" do-      forM_ invalid \(TestData name tid _ _) -> it name do+      forM_ invalid \(TestData n tid _ _) -> it n do         case decode @TypeIDV4 (fromString $ show tid) of-          Nothing  -> pure ()-          Just tid -> expectationFailure $ "Parsed TypeID: " ++ show tid+          Nothing -> pure ()+          Just t  -> expectationFailure $ "Parsed TypeID: " ++ show t     describe "Invalid JSON key" do-      forM_ invalid \(TestData name tid _ _) -> it name do+      forM_ invalid \(TestData n tid _ _) -> it n do         case decode @(Map TypeIDV4 Int) (fromString $ "{" ++ show tid ++ ":" ++ "114514" ++ "}") of-          Nothing  -> pure ()-          Just tid -> expectationFailure "Invalid TypeID key shouldn't be parsed!"+          Nothing -> pure ()+          _       -> expectationFailure "Invalid TypeID key shouldn't be parsed!"    describe "Test invalid.json" do-    forM_ invalid \(TestData name tid _ _) -> it name do+    forM_ invalid \(TestData n tid _ _) -> it n do       case string2ID @TypeIDV4 tid of-        Left _    -> pure ()-        Right tid -> expectationFailure $ "Parsed TypeID: " ++ show tid+        Left _  -> pure ()+        Right t -> expectationFailure $ "Parsed TypeID: " ++ show t    describe "Test valid.json (TypeIDV4 as literal)" do-    forM_ valid \(TestData name tid (Just prefix) (Just uuid)) -> it name do+    forM_ valid \(TestData n tid (Just pref) (Just uid)) -> it n do       case string2ID @TypeIDV4 tid of-        Left err  -> expectationFailure $ "Parse error: " ++ show err-        Right tid -> do-          getPrefix tid `shouldBe` prefix-          show (getUUID tid) `shouldBe` uuid+        Left err -> expectationFailure $ "Parse error: " ++ show err+        Right t  -> do+          getPrefix t `shouldBe` pref+          show (getUUID t) `shouldBe` uid    describe "Test valid.json (TypeIDV4 as JSON)" do-    forM_ validUUID \(TestDataUUID name tid prefix uuid) -> it name do-      getPrefix tid `shouldBe` prefix-      getUUID tid `shouldBe` uuid+    forM_ validUUID \(TestDataUUID n tid pref uid) -> it n do+      getPrefix tid `shouldBe` pref+      getUUID tid `shouldBe` uid    describe "Generate KindIDV4 with 'Symbol' prefixes" do     it "can generate KindIDV4 with prefix" do@@ -607,7 +604,7 @@         Right kid -> getPrefix kid `shouldBe` "mmzk"     it "cannot parse KindID into wrong prefix" do       case string2ID @(KindIDV4 "foo") "mmzk_5hjpeh96458fct8t49fnf9farw" of-        Left err  -> pure ()+        Left _    -> pure ()         Right kid -> expectationFailure $ "Parsed TypeID: " ++ show kid    describe "Generate KindIDV4 with custom data kind prefixes" do@@ -620,7 +617,7 @@         Right kid -> getPrefix kid `shouldBe` "user"     it "cannot parse KindIDV4 into wrong prefix" do       case string2ID @(KindIDV4 'Comment) "user_00041061050r3gg28a1c60t3gf" of-        Left err  -> pure ()+        Left _    -> pure ()         Right kid -> expectationFailure $ "Parsed KindIDV4: " ++ show kid    describe "Binary instance for TypeIDV4 and KindIDV4" do@@ -664,13 +661,14 @@   invalid   <- runIO (BSL.readFile "test/invalid.json" >>= throwDecode :: IO [TestData])   valid     <- runIO (BSL.readFile "test/valid.json" >>= throwDecode :: IO [TestData])   validUUID <- runIO (BSL.readFile "test/valid.json" >>= throwDecode :: IO [TestDataUUID TypeIDV5])-  uuid      <- runIO nextRandom+     describe "Generate TypeIDV5" do+    uid <- runIO nextRandom     it "can generate TypeIDV5 with prefix" do-      tid <- withCheck $ genID @TypeIDV5 "mmzk" uuid [11, 45, 14]+      tid <- withCheck $ genID @TypeIDV5 "mmzk" uid [11, 45, 14]       getPrefix tid `shouldBe` "mmzk"     it "can generate TypeIDV5 without prefix" do-      tid <- withCheck $ genID @TypeIDV5 "" uuid [11, 45, 14]+      tid <- withCheck $ genID @TypeIDV5 "" uid [11, 45, 14]       getPrefix tid `shouldBe` ""     it "can parse TypeIDV5 from String" do       case string2ID @TypeIDV5 "mmzk_5hjpeh96458fct8t49fnf9farw" of@@ -678,6 +676,7 @@         Right tid -> getPrefix tid `shouldBe` "mmzk"    describe "Parse TypeIDV5" do+    uid <- runIO nextRandom     let invalidPrefixes = [ ("caps", "PREFIX")                           , ("numeric", "12323")                           , ("symbols", "pre.fix")@@ -685,9 +684,9 @@                           , ("long", "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz")                           , ("ascii", "château") ]     describe "can detect invalid prefix" do-      forM_ invalidPrefixes \(reason, prefix) -> it reason do-        genID @TypeIDV5 prefix  uuid [11, 45, 14] `shouldThrow` anyTypeIDError-        case decorate @TypeIDV5 prefix nil of+      forM_ invalidPrefixes \(reason, pref) -> it reason do+        genID @TypeIDV5 pref uid [11, 45, 14] `shouldThrow` anyTypeIDError+        case decorate @TypeIDV5 pref nil of           Left _  -> pure ()           Right _ -> expectationFailure "Should not be able to decorate with invalid prefix"     let invalidSuffixes = [ ("spaces", " ")@@ -710,72 +709,74 @@                         , ("ten", "0000000000000000000000000a", "00000000-0000-0000-0000-00000000000a")                         , ("sixteen", "0000000000000000000000000g", "00000000-0000-0000-0000-000000000010")                         , ("thirty-two", "00000000000000000000000010", "00000000-0000-0000-0000-000000000020") ]-    forM_ specialValues \(reason, tid, uuid) -> it reason do+    forM_ specialValues \(reason, tid, uid) -> it reason do       case string2ID @TypeIDV5 tid of-        Left err  -> expectationFailure $ "Parse error: " ++ show err-        Right tid -> show (getUUID tid) `shouldBe` uuid+        Left err -> expectationFailure $ "Parse error: " ++ show err+        Right t  -> show (getUUID t) `shouldBe` uid    describe "TypeIDV5 valid JSON instances" do     it "Decode and then encode should be identity" do-      tid  <- genID @TypeIDV5 "mmzk" uuid [11, 45, 14]-      tid' <- genID @TypeIDV5 "foo" uuid [11, 45, 14]+      uid <- nextRandom+      tid  <- genID @TypeIDV5 "mmzk" uid [11, 45, 14]+      tid' <- genID @TypeIDV5 "foo" uid [11, 45, 14]       let mapping = M.fromList [(tid, tid')]       let json    = encode mapping       decode json `shouldBe` Just mapping       fmap encode (decode @(Map TypeIDV5 TypeIDV5) json) `shouldBe` Just json     describe "Valid JSON value" do-      forM_ valid \(TestData name tid (Just prefix) (Just uuid)) -> it name do+      forM_ valid \(TestData n tid (Just pref) (Just uid)) -> it n do         case decode @TypeIDV5 (fromString $ show tid) of-          Nothing  -> expectationFailure "Parse JSON failed!"-          Just tid -> do-            getPrefix tid `shouldBe` prefix-            show (getUUID tid) `shouldBe` uuid+          Nothing -> expectationFailure "Parse JSON failed!"+          Just t  -> do+            getPrefix t `shouldBe` pref+            show (getUUID t) `shouldBe` uid     describe "Valid JSON key" do-      forM_ valid \(TestData name tid (Just prefix) (Just uuid)) -> it name do+      forM_ valid \(TestData n tid (Just pref) (Just uid)) -> it n do         case decode @(Map TypeIDV5 Int) (fromString $ "{" ++ show tid ++ ":" ++ "114514" ++ "}") of           Nothing      -> expectationFailure "Parse JSON failed!"           Just mapping -> do-            let (tid, _) = M.elemAt 0 mapping-            getPrefix tid `shouldBe` prefix-            show (getUUID tid) `shouldBe` uuid+            let (t, _) = M.elemAt 0 mapping+            getPrefix t `shouldBe` pref+            show (getUUID t) `shouldBe` uid    describe "TypeIDV5 invalid JSON instances" do     describe "Invalid JSON value" do-      forM_ invalid \(TestData name tid _ _) -> it name do+      forM_ invalid \(TestData n tid _ _) -> it n do         case decode @TypeIDV5 (fromString $ show tid) of-          Nothing  -> pure ()-          Just tid -> expectationFailure $ "Parsed TypeID: " ++ show tid+          Nothing -> pure ()+          Just t  -> expectationFailure $ "Parsed TypeID: " ++ show t     describe "Invalid JSON key" do-      forM_ invalid \(TestData name tid _ _) -> it name do+      forM_ invalid \(TestData n tid _ _) -> it n do         case decode @(Map TypeIDV5 Int) (fromString $ "{" ++ show tid ++ ":" ++ "114514" ++ "}") of-          Nothing  -> pure ()-          Just tid -> expectationFailure "Invalid TypeID key shouldn't be parsed!"+          Nothing -> pure ()+          _       -> expectationFailure "Invalid TypeID key shouldn't be parsed!"    describe "Test invalid.json" do-    forM_ invalid \(TestData name tid _ _) -> it name do+    forM_ invalid \(TestData n tid _ _) -> it n do       case string2ID @TypeIDV5 tid of         Left _    -> pure ()-        Right tid -> expectationFailure $ "Parsed TypeID: " ++ show tid+        Right t -> expectationFailure $ "Parsed TypeID: " ++ show t    describe "Test valid.json (TypeIDV5 as literal)" do-    forM_ valid \(TestData name tid (Just prefix) (Just uuid)) -> it name do+    forM_ valid \(TestData n tid (Just pref) (Just uid)) -> it n do       case string2ID @TypeIDV5 tid of-        Left err  -> expectationFailure $ "Parse error: " ++ show err-        Right tid -> do-          getPrefix tid `shouldBe` prefix-          show (getUUID tid) `shouldBe` uuid+        Left err -> expectationFailure $ "Parse error: " ++ show err+        Right t  -> do+          getPrefix t `shouldBe` pref+          show (getUUID t) `shouldBe` uid    describe "Test valid.json (TypeIDV5 as JSON)" do-    forM_ validUUID \(TestDataUUID name tid prefix uuid) -> it name do-      getPrefix tid `shouldBe` prefix-      getUUID tid `shouldBe` uuid+    forM_ validUUID \(TestDataUUID n tid pref uid) -> it n do+      getPrefix tid `shouldBe` pref+      getUUID tid `shouldBe` uid    describe "Generate KindIDV5 with 'Symbol' prefixes" do+    uid <- runIO nextRandom     it "can generate KindIDV5 with prefix" do-      kid <- withCheck $ genID @(KindIDV5 "mmzk") uuid [11, 45, 14]+      kid <- withCheck $ genID @(KindIDV5 "mmzk") uid [11, 45, 14]       getPrefix kid `shouldBe` "mmzk"     it "can generate KindIDV5 without prefix" do-      kid <- withCheck $ genID @(KindIDV5 "") uuid [11, 45, 14]+      kid <- withCheck $ genID @(KindIDV5 "") uid [11, 45, 14]       getPrefix kid `shouldBe` ""     it "can parse KindID from String" do       case string2ID @(KindIDV5 "mmzk") "mmzk_5hjpeh96458fct8t49fnf9farw" of@@ -783,7 +784,7 @@         Right kid -> getPrefix kid `shouldBe` "mmzk"     it "cannot parse KindID into wrong prefix" do       case string2ID @(KindIDV5 "foo") "mmzk_5hjpeh96458fct8t49fnf9farw" of-        Left err  -> pure ()+        Left _    -> pure ()         Right kid -> expectationFailure $ "Parsed TypeID: " ++ show kid    describe "Generate KindIDV5 with custom data kind prefixes" do@@ -796,26 +797,28 @@         Right kid -> getPrefix kid `shouldBe` "user"     it "cannot parse KindIDV5 into wrong prefix" do       case string2ID @(KindIDV5 'Comment) "user_00041061050r3gg28a1c60t3gf" of-        Left err  -> pure ()+        Left _    -> pure ()         Right kid -> expectationFailure $ "Parsed KindIDV5: " ++ show kid    describe "Binary instance for TypeIDV5 and KindIDV5" do+    uid <- runIO nextRandom     it "has correct binary instance for TypeIDV5" do-      tids <- withChecks $ genIDs @TypeIDV5 "abcdefghijklmnopqrstuvwxyz" uuid [11, 45, 14] 114+      tids <- withChecks $ genIDs @TypeIDV5 "abcdefghijklmnopqrstuvwxyz" uid [11, 45, 14] 114       forM_ tids \tid -> do         let bytes = runPut (put tid)         let tid'  = runGet get bytes         tid' `shouldBe` tid     it "has correct binary instance for KindIDV5" do-      kids <- withChecks $ genIDs @(KindIDV5 "abcdefghijklmnopqrstuvwxyz") uuid [11, 45, 14] 114+      kids <- withChecks $ genIDs @(KindIDV5 "abcdefghijklmnopqrstuvwxyz") uid [11, 45, 14] 114       forM_ kids \kid -> do         let bytes = runPut (put kid)         let kid'  = runGet get bytes         kid' `shouldBe` kid    describe "Storable instance for TypeIDV5 and KindIDV5" do+    uid <- runIO nextRandom     it "has correct Storable instance for TypeIDV5" do-      tids <- withChecks $ genIDs @TypeIDV5 "abcdefghijklmnopqrstuvwxyz" uuid [11, 45, 14] 114+      tids <- withChecks $ genIDs @TypeIDV5 "abcdefghijklmnopqrstuvwxyz" uid [11, 45, 14] 114       forM_ tids \tid -> do         ptr   <- new tid         tid'  <- peek ptr@@ -825,7 +828,7 @@         tid'' `shouldBe` tid'         free ptr     it "has correct Storable instance for KindIDV5" do-      kids <- withChecks $ genIDs @(KindIDV5 "abcdefghijklmnopqrstuvwxyz") uuid [11, 45, 14] 114+      kids <- withChecks $ genIDs @(KindIDV5 "abcdefghijklmnopqrstuvwxyz") uid [11, 45, 14] 114       forM_ kids \kid -> do         ptr   <- new kid         kid'  <- peek ptr