A phantom type parameter appears in a type declaration but not in the runtime representation of its values. It carries compile-time identity without adding stored data. In a key–value database language, the phantom parameter `db` distinguishes databases while operations still use `ByteString` keys and values. section 11.1.2
Use phantom types to make otherwise similar interfaces harder to misuse:
- Declare empty marker types such as `ValuesDB` and `AstroDB`, then associate each with a database name through a `DB` type class. section 11.1.2
- Parameterize the database language as `KVDBL db`, so a computation intended for one database cannot be silently substituted for another. section 11.1.2
- Select the marker explicitly with type applications such as `@ValuesDB` or with a direct type annotation. section 11.1.2
- Apply the same technique to service APIs: an `api` parameter selects an HTTP or TCP type-class instance, although mismatched runtime channels can still require error handling. section 13.2.6
Listing 11.3 Basic string-based key–value database interface
type KVDBKey = ByteString
type KVDBValue = ByteString
data KVDBF next where
Save :: KVDBKey
-> KVDBValue
-> (DBResult () -> next)
-> KVDBF next
Load :: KVDBKey
-> (DBResult KVDBValue -> next)
-> KVDBF next
type KVDBL db = Free KVDBF #1
save :: KVDBKey -> KVDBValue -> KVDBL db (DBResult ()) #2
load :: KVDBKey -> KVDBL db (DBResult KVDBValue) #2
Inspect the minimal `KVDBL db` interface to see the phantom database parameter attached to a free-monad language whose values contain only keys, values, and results. section 11.1.2

