taiwan-id 0.1.1.0 → 0.1.1.1
raw patch · 3 files changed
+296/−30 lines, 3 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
Files
- CHANGELOG.md +4/−0
- README.md +282/−17
- taiwan-id.cabal +10/−13
CHANGELOG.md view
@@ -1,3 +1,7 @@+# 0.1.1.1++- Improved `README.md` and package description.+ # 0.1.1.0 - Added a `taiwan-id` command-line tool with three commands:
README.md view
@@ -7,27 +7,292 @@ https://img.shields.io/badge/Development%20Branch-API%20Documentation-225577 )](https://jonathanknowles.github.io/taiwan-id/) -This package provides a Haskell implementation of Taiwan's uniform-identification number format.+This package provides both a **Haskell library** and a **command-line tool**+(CLI) for working with identification numbers issued to residents of Taiwan, as+well as the other territories of the Republic of China (ROC), including Kinmen,+Matsu, and Penghu. -This number format is used by both National Identification Cards (國民身分證)-and Resident Certificates (居留證) issued by the Republic of China (ROC)-government to individuals, with numbers assigned under each system occupying-disjoint parts of the same identifier space.+# Contents +1. [Background](#background)+2. [Library](#library)+ 1. [Usage](#usage)+ 1. [Parsing](#parsing)+ 1. [Run-time parsing](#run-time-parsing)+ 2. [Compile-time parsing](#compile-time-parsing)+ 2. [Inspecting attributes](#inspecting-attributes)+ 2. [Design philosophy](#design-philosophy)+ 1. [Correctness by construction](#correctness-by-construction)+ 2. [Lawful class instances](#lawful-class-instances)+3. [Command-line tool](#command-line-tool)+ 1. [Installation](#installation)+ 2. [Usage](#usage-1)+ 1. [Validation](#validation)+ 2. [Decoding](#decoding)+ 3. [Generation](#generation)+4. [References](#references)++# Background++The ROC government issues identification numbers to individuals on two types of+identification card:++1. **National Identification Cards** (國民身分證), issued by the Household+ Registration Office (戶政事務所);+2. **Resident Certificates** (居留證), issued by the National Immigration+ Agency (移民署).++Although these two card types are distinct, both share a common identification+number format, with numbers assigned under each system occupying disjoint parts+of the same number space.+ Each identification number consists of a single uppercase letter followed by-nine decimal digits, with the final digit serving as a checksum calculated-according to a standard algorithm.+nine decimal digits: -Example: `A123456789`+```+A 1 2 3 4 5 6 7 8 9+│ │ └───────────┘ │+│ │ serial number └── checksum+│ └── gender and issuer+└──── region+``` -This package offers a library with functions for validating, decoding, and-encoding these numbers, as well as a command-line tool for working with them-interactively.+The leading letter encodes the **region** in which the number was issued. The+second digit jointly encodes the holder's **gender** and the **issuing+authority** (either the Household Registration Office or the National+Immigration Agency). The final digit serves as a **checksum**. -For more details, see:+# Library -* https://zh.wikipedia.org/wiki/中華民國國民身分證-* https://zh.wikipedia.org/wiki/中華民國居留證-* https://en.wikipedia.org/wiki/National_identification_card_(Taiwan)-* https://en.wikipedia.org/wiki/Resident_certificate+## Usage++Usage examples assume the following extensions and imports:++```haskell+>>> :set -XDataKinds+>>> :set -XOverloadedStrings+>>> :set -XTypeApplications+>>> import Taiwan.ID qualified as ID+>>> import Taiwan.ID.Region qualified as ID.Region+>>> import Taiwan.ID.Language (Language (..))+```++### Parsing++At the heart of the library is the ability to parse and validate identification+numbers from textual input, accepting only those that are well-formed according+to the standard.++The library provides two functions for this purpose: `fromText`, which parses+a `Text` value at run time, and `fromSymbol`, which parses a type-level+`Symbol` at compile time.++#### Run-time parsing++To parse an `ID` from `Text`, use the `fromText` function:++```haskell+>>> ID.fromText "A123456789"+Right (ID.fromSymbol @"A123456789")+```++If the supplied `Text` is not a valid identification number, `fromText` returns+a structured error describing why the number is invalid:++```haskell+>>> ID.fromText "A1234"+Left InvalidLength++>>> ID.fromText "A123456780"+Left InvalidChecksum++>>> ID.fromText "A1_3456789"+Left (InvalidChar (CharIndex 2) (CharRange '0' '9'))+```++#### Compile-time parsing++The `fromSymbol` function constructs an `ID` from a type-level `Symbol`,+validated entirely at **compile time**:++```haskell+>>> ID.fromSymbol @"A123456789"+ID.fromSymbol @"A123456789"+```++Instead of run-time errors, invalid symbols are reported as **type errors**:++```haskell+>>> ID.fromSymbol @"A1234"+error:+ An ID must have exactly 10 characters.++>>> ID.fromSymbol @"A123456780"+error:+ ID has invalid checksum.++>>> ID.fromSymbol @"A_23456789"+error:+ "A_23456789"+ ^+ Character at this position must be a digit from the set {1, 2, 8, 9}.+```++### Inspecting attributes++Once you have a valid `ID`, you can inspect its attributes:++```haskell+>>> let i = ID.fromSymbol @"A123456789"++>>> ID.getIssuer i+HouseholdRegistrationOffice++>>> ID.getGender i+Male++>>> ID.Region.toText English (ID.getRegion i)+"Taipei City"+```++## Design philosophy++### Correctness by construction++The library is built around the principle that **invalid states should be+unrepresentable**. This applies not just to the top-level `ID` type, but to+every component type used to construct it.++An `ID` value is stored as a record of smaller types, each of which admits+only the values that are structurally valid at its position:++- `c0 :: Letter` — one of the 26 uppercase letters+- `c1 :: Digit1289` — one of the digits `{1, 2, 8, 9}`+- `c2 .. c8 :: Digit` — digits in the range `[0 .. 9]`++The checksum digit is **not stored** in an `ID` value at all. Instead, it is+computed on demand from the other fields, which means a stored `ID` value is+always internally consistent — there is no way to construct one with a+mismatched checksum.++This means that the type of `ID` itself acts as a proof of validity. If you+hold a value of type `ID`, you know — without any further checking — that it+represents a well-formed identification number.++### Lawful class instances++The existence of `fromSymbol` makes it possible to offer `Show` and `Read`+instances that are genuinely lawful in a way that naive implementations often+are not.++The `Show` instance produces a valid Haskell expression:++```haskell+>>> show (ID.fromSymbol @"A123456789")+"ID.fromSymbol @\"A123456789\""+```++This output is not merely human-readable — it is a well-typed Haskell+expression that, when evaluated, produces the original value. As a result,+`read . show` roundtrips faithfully:++```haskell+>>> let i = ID.fromSymbol @"A123456789"+>>> read (show i) == i+True+```++# Command-line tool++## Installation++First, install the [Haskell toolchain](https://www.haskell.org/). One popular+way to do this is via [`ghcup`](https://www.haskell.org/ghcup/).++Then run:++```+cabal install taiwan-id+```++## Usage++The `taiwan-id` command-line tool provides three commands.++### Validation++The `validate` command checks whether an identification number is valid.++If an identification number is valid, it exits with code `0` and no further+output:++```+$ taiwan-id validate P833485645+```++If an identification number is not valid, it exits with code `1` and emits an+error message to `stderr`:++```+$ taiwan-id validate N140792413+Invalid checksum.++$ taiwan-id validate I96342+Invalid length.+An identification number must be exactly 10 characters in length.++$ taiwan-id validate C25171445&+Invalid character:+C25171445&+ ^+Character at this position must be a character in the range [0 .. 9].+```++### Decoding++The `decode` command decodes the attributes of an identification number.+Output is available in English (the default) or Chinese:++```+$ taiwan-id decode H271789449+Issuer: Household Registration Office+Gender: Female+Region: Taoyuan City++$ taiwan-id decode Y175974499 --language=Chinese+核發機關:戶政事務所+性別 :男性+地區 :陽明山+```++### Generation++The `generate` command generates one or more random identification numbers:++```+$ taiwan-id generate+E218711091++$ taiwan-id generate --count 4+R886773836+C129535585+E892135379+K207816302+```++For deterministic output, you can specify a seed:++```+$ taiwan-id generate --count 4 --seed 888+X207421526+H891911565+K935490929+O198217491+```++# References++- [National identification card (Taiwan)](https://en.wikipedia.org/wiki/National_identification_card_(Taiwan)) — Wikipedia+- [Resident certificate](https://en.wikipedia.org/wiki/Resident_certificate) — Wikipedia+- [中華民國國民身分證](https://zh.wikipedia.org/wiki/中華民國國民身分證) — 維基百科+- [中華民國居留證](https://zh.wikipedia.org/wiki/中華民國居留證) — 維基百科
taiwan-id.cabal view
@@ -1,7 +1,7 @@ cabal-version: 3.0 name: taiwan-id-version: 0.1.1.0-synopsis: Implementation of Taiwan's uniform ID number format.+version: 0.1.1.1+synopsis: Library and CLI for working with ID numbers issued in Taiwan. category: Identification homepage: https://github.com/jonathanknowles/taiwan-id#readme bug-reports: https://github.com/jonathanknowles/taiwan-id/issues@@ -14,13 +14,14 @@ description: - This package provides a Haskell implementation of Taiwan's uniform- identification number format.+ This package provides both a library and a command-line tool (CLI) for+ working with identification numbers issued to residents of Taiwan, as well+ as the other territories of the Republic of China (ROC), including Kinmen,+ Matsu, and Penghu. - This number format is used by both National Identification Cards (國民身分證)- and Resident Certificates (居留證) issued by the Republic of China (ROC)- government to individuals, with numbers assigned under each system occupying- disjoint parts of the same identifier space.+ Identification numbers are issued under two systems — National Identification+ Cards (國民身分證) and Resident Certificates (居留證) — with numbers assigned+ under each system occupying disjoint parts of the same number space. Each identification number consists of a single uppercase letter followed by nine decimal digits, with the final digit serving as a checksum calculated@@ -28,11 +29,7 @@ Example: `A123456789` - This package offers a library with functions for validating, decoding, and- encoding these numbers, as well as a command-line tool for working with them- interactively.-- See the "Taiwan.ID" module to get started.+ See `README.md` or the "Taiwan.ID" module to get started. For more details, see: