Since the values of type parameters in a phantom type may be unused, they are often used in combination with empty types.

Phantom types are nearly always either newtype or data. It is possible to create "phantom type synonyms", but they are usually useless: since synonyms are expanded at compile time, the phantom type variable will be discarded.

This technique is perfect for e.g. escaping user input to a web application. We can ensure with zero overhead that the data is escaped once and only once everywhere that it needs to be, or else we get a compile-time error.

I believe this technique is used when trying to interface
with a language that would cause a runtime exception if the types
were wrong but would have a go at running the expression first.
(They use it in the context of SQL but I have also seen it in the
context of FLI work.)

-- ChrisAngus

A foundation for embedded languages provides some formal background for embedding typed languages in Haskell, and also its references give a fairly comprehensive survey of uses of phantom types and related techniques.