{"id":15288098,"url":"https://github.com/mmzk1526/mmzk-typeid","last_synced_at":"2025-10-29T23:13:00.301Z","repository":{"id":179262997,"uuid":"663214187","full_name":"MMZK1526/mmzk-typeid","owner":"MMZK1526","description":"A TypeID and UUIDv7 implementation in Haskell","archived":false,"fork":false,"pushed_at":"2024-11-21T22:01:33.000Z","size":407,"stargazers_count":7,"open_issues_count":0,"forks_count":3,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-04-07T04:07:07.928Z","etag":null,"topics":["haskell","haskell-library","typeid","uuid","uuid-generator","uuid-v7"],"latest_commit_sha":null,"homepage":"https://hackage.haskell.org/package/mmzk-typeid","language":"Haskell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/MMZK1526.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-07-06T20:07:55.000Z","updated_at":"2024-12-19T12:09:11.000Z","dependencies_parsed_at":null,"dependency_job_id":"d22b30a1-b00f-4b7c-b8a5-e765906a4928","html_url":"https://github.com/MMZK1526/mmzk-typeid","commit_stats":null,"previous_names":["mmzk1526/mmzk-typeid"],"tags_count":16,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MMZK1526%2Fmmzk-typeid","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MMZK1526%2Fmmzk-typeid/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MMZK1526%2Fmmzk-typeid/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MMZK1526%2Fmmzk-typeid/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/MMZK1526","download_url":"https://codeload.github.com/MMZK1526/mmzk-typeid/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248674659,"owners_count":21143760,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["haskell","haskell-library","typeid","uuid","uuid-generator","uuid-v7"],"created_at":"2024-09-30T15:44:07.315Z","updated_at":"2025-10-29T23:12:57.802Z","avatar_url":"https://github.com/MMZK1526.png","language":"Haskell","funding_links":[],"categories":[],"sub_categories":[],"readme":"# mmzk-typeid\n\n## Introduction\n\nA [TypeID](https://github.com/jetpack-io/typeid) implementation in Haskell. It is a \"type-safe, K-sortable, globally unique identifier\" extended on top of UUIDv7.\n\nTypeIDs are canonically encoded as lowercase strings consisting of three parts:\n\n1. A type prefix (at most 63 characters in all lowercase snake_case ASCII [a-z_]);\n2. An underscore '_' separator;\n3. A 128-bit UUIDv7 encoded as a 26-character string using a modified base32 encoding.\n\nFor more information, please check out [specification v0.3.0](https://github.com/jetpack-io/typeid/blob/main/README.md).\n\nIt also serves as a (temporary) UUIDv7 implementation in Haskell, since there are no official ones yet.\n\nIf you notice any issues or have any suggestions, please feel free to open an issue or contact me via email.\n\n## Highlights\n\nIn addition to the features provided by [TypeID](https://github.com/jetpack-io/typeid), this implementation also supports:\n\n1. Generating TypeIDs in a batch. They are guaranteed to have the same timestamp (up to the first 32768 ids) and of ascending order;\n2. 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 an invalid prefix, the code won't compile, avoiding the need for runtime checks;\n3. Support TypeID with other UUID versions. Currently v7 (default), v1,  v4, and v5 are supported.\n\n## Quick start\n\n```Haskell\n{-# LANGUAGE OverloadedStrings #-}\n\nimport           Control.Exception\nimport           Data.TypeID (TypeID)\nimport qualified Data.TypeID as TID\n\nmain :: IO ()\nmain = do\n\n  -- Make a TypeID with prefix 'mmzk':\n  typeID \u003c- TID.genTypeID \"mmzk\"\n  putStrLn $ TID.toString typeID\n\n  -- Get components from the TypeID:\n  let prefix = TID.getPrefix typeID -- \"mmzk\"\n      uuid   = TID.getUUID typeID\n      time   = TID.getTime typeID -- A 'Word64' representing the timestamp in milliseconds\n\n  -- Make a TypeID without prefix:\n  typeID' \u003c- TID.genTypeID \"\"\n  print typeID'\n\n  -- Make 10 TypeIDs in a batch. They are guaranteed to have the same timestamp and of ascending order:\n  typeIDs \u003c- TID.genTypeIDs \"mmzk\" 10\n  mapM_ print typeIDs\n\n  -- Parse a TypeID from string:\n  case TID.parseString \"mmzk_01h455vb4pex5vsknk084sn02q\" of\n    Left err     -\u003e throwIO err\n    Right typeID -\u003e print typeID\n```\n\nFor a full list of functions on `TypeID`, see [Data.TypeID](https://hackage.haskell.org/package/mmzk-typeid/docs/Data-TypeID.html).\n\n## More Usages\n\n### TypeID with other UUID Versions\n\nWe also support TypeID using some other versions of `UUID`, including v1, v4 and v5, which loses the monoticity property. To use it, simply import `Data.TypeID.V4` instead of `Data.TypeID`. The following is an example using v4:\n\n```Haskell\n{-# LANGUAGE OverloadedStrings #-}\n\nimport           Control.Exception\nimport           Data.TypeID.V4 (TypeIDV4)\nimport qualified Data.TypeID.V4 as TID\n\nmain :: IO ()\nmain = do\n\n  -- Make a TypeID with prefix 'mmzk':\n  typeID \u003c- TID.genTypeID \"mmzk\"\n  putStrLn $ TID.toString typeID\n\n  -- Get components from the TypeID:\n  let prefix = TID.getPrefix typeID -- \"mmzk\"\n      uuid   = TID.getUUID typeID\n\n  -- Make a TypeID without prefix:\n  typeID' \u003c- TID.genTypeID \"\"\n  print typeID'\n\n  -- Parse a TypeID from string:\n  case TID.parseString \"mmzk_5hjpeh96458fct8t49fnf9farw\" of\n    Left err     -\u003e throwIO err\n    Right typeID -\u003e print typeID\n```\n\n### Type-level TypeID (KindID)\nWhen 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.\n\nOf 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 one table in the database.\n\nFor example, suppose we have a function that takes a KindID with the prefix \"user\", it may have a signature like this: `f :: KindID \"user\" -\u003e IO ()`.\n\nThen if we try to pass in a KindID with the prefix \"post\", the compiler will complain, thus removing the runtime check and the associated overhead.\n\nAll the prefixes are type-checked at compile time, so if we try to pass in invalid prefixes, the compiler (again) will complain.\n\n```Haskell\n{-# LANGUAGE DataKinds #-}\n{-# LANGUAGE OverloadedStrings #-}\n{-# LANGUAGE TypeApplications #-}\n\nimport           Control.Exception\nimport           Data.KindID (KindID)\nimport qualified Data.KindID as KID\n\nmain :: IO ()\nmain = do\n\n  -- Make a KindID with prefix 'mmzk':\n  kindID \u003c- KID.genKindID @\"mmzk\" -- Has type `KindID \"mmzk\"`\n  putStrLn $ KID.toString kindID\n\n  -- Get components from the KindID:\n  let prefix = KID.getPrefix kindID -- \"mmzk\"\n      uuid   = KID.getUUID kindID\n      time   = KID.getTime kindID -- A 'Word64' representing the timestamp in milliseconds\n\n  -- Make a KindID without prefix:\n  kindID' \u003c- KID.genKindID @\"\" -- Has type `KindID \"\"`\n  print kindID'\n\n  -- Make 10 KindIDs in a batch. They are guaranteed to have the same timestamp and of ascending order:\n  kindIDs \u003c- KID.genKindIDs @\"mmzk\" 10\n  mapM_ print kindIDs\n\n  -- Parse a KindID from string:\n  case KID.parseString @\"mmzk\" \"mmzk_01h455vb4pex5vsknk084sn02q\" of\n    Left err     -\u003e throwIO err\n    Right kindID -\u003e print kindID\n```\n\nFor a full list of functions on `KindID`, see [Data.KindID](https://hackage.haskell.org/package/mmzk-typeid/docs/Data-KindID.html).\n\n### Functions with More General Types\n`TypeID` and `KindID` shares many functions with the same name and functionality. So far, we are using qualified imports to diffentiate them (*e.g* `KID.fromString` and `TID.fromString`). Alternatively, we can use the methods of `IDConv` to use the same functions for both `TypeID` and `KindID`.\n\n```Haskell\n{-# LANGUAGE DataKinds #-}\n{-# LANGUAGE OverloadedStrings #-}\n{-# LANGUAGE TypeApplication #-}\n\nimport           Control.Exception\nimport           Data.KindID\nimport           Data.TypeID\n\nmain :: IO ()\nmain = do\n\n  -- Make a TypeID with prefix 'mmzk':\n  typeID \u003c- genID @TypeID \"mmzk\"\n  print typeID\n\n  -- Make a KindID with prefix 'mmzk':\n  kindID \u003c- genID @(KindID \"mmzk\")\n  print kindID\n\n  -- Parse a TypeID from string:\n  case string2ID \"mmzk_01h455vb4pex5vsknk084sn02q\" :: Maybe TypeID of\n    Left err     -\u003e throwIO err\n    Right typeID -\u003e print typeID\n\n  -- Parse a KindID from string:\n  case string2ID \"mmzk_01h455vb4pex5vsknk084sn02q\" :: Maybe (KindID \"mmzk\") of\n    Left err     -\u003e throwIO err\n    Right kindID -\u003e print kindID\n\n  -- Parse a KindID from string (wrong prefix):\n  case string2ID \"mmzk_01h455vb4pex5vsknk084sn02q\" :: Maybe (KindID \"foo\") of\n    Left err     -\u003e throwIO err -- Will throw here as the prefix matches not\n    Right kindID -\u003e print kindID\n```\n\nWe no longer need to use qualified imports, but on the down side, we need to add explicit type annotations. Therefore it is a matter of preference.\n\nNote that with the class methods, the type application with `Symbol` no longer works as the full type must be provided. For example, `string2ID @\"mmzk\" \"mmzk_01h455vb4pex5vsknk084sn02q\"` will not compile.\n\nFor a full list of these functions, see [Data.TypeID.Class](https://hackage.haskell.org/package/mmzk-typeid/docs/Data-TypeID-Class.html).\n\n### KindID with Data Kinds\nInstead of using raw `Symbol`s as `KindID` prefixes, we can also define our custom data type for better semantics.\n\nFor example, suppose we have three tables for users, posts, and comments, and each table has a unique prefix, we can design the structure as following:\n\n```Haskell\n{-# LANGUAGE DataKinds #-}\n{-# LANGUAGE OverloadedStrings #-}\n{-# LANGUAGE PolyKinds #-}\n{-# LANGUAGE TypeApplications #-}\n\nimport           Data.KindID\nimport           Data.KindID.Class\n\ndata Prefix = User | Post | Comment\n\ninstance ToPrefix 'User where\n  type PrefixSymbol 'User = \"user\"\n\ninstance ToPrefix 'Post where\n  type PrefixSymbol 'Post = \"post\"\n\ninstance ToPrefix 'Comment where\n  type PrefixSymbol 'Comment = \"comment\"\n```\n\nNow we can use `Prefix` as a prefix for `KindID`s, *e.g.*\n\n```Haskell\nmain :: IO ()\nmain = do\n  -- ...\n  userID    \u003c- genKindID @'User -- Same as genKindID @\"user\"\n  postID    \u003c- genKindID @'Post -- Same as genKindID @\"post\"\n  commentID \u003c- genKindID @'Comment -- Same as genKindID @\"comment\"\n  -- ...\n```\n\nFor more information, see [Data.KindID.Class](https://hackage.haskell.org/package/mmzk-typeid/docs/Data-KindID-Class.html).\n\n## Note\nFunctions not explicitly exported are considered internal and are subjected to changes.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmmzk1526%2Fmmzk-typeid","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmmzk1526%2Fmmzk-typeid","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmmzk1526%2Fmmzk-typeid/lists"}