Phantom types are ordinary
newtype types with an extra parameter on the left side of the data definition which is not used on the right side.
data PhantomizedType a = PhantomizedType
But why is this useful?
In some cases functions, within an implementation excepting that data comes in an explicit state. For example if a web page takes user input. This input needs to be sanitized to remove malicious input. All functions which are called after the sanitize function expecting that the information is already sanitized. This information is usually shipped implicit via control structures, flags, etc. The problem: You need to detect if data is not well formed and handle this error on runtime.
Phantom types eliminate this problem + lifting error detection to compile time!
An instructional example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
As you can see in this example data is
stdout. This is just fine.
Message is a type synonym to
send should only send an encrypted string to
decrypt should ensure that
Message is encrypted and not cleartext. Achieving this is tedious and error prone. With phantom types we can add this information to the type system which checks at compile time(!) that everything is in right order.
Rewriting the message declaration using Phantom types is easy:
data Message a = Message String data Encrypted data Cleartext newMessage :: String -> Message Cleartext newMessage = Message
In combination with
empty types we’re able to add information about the state of the
Message to the type system (awesome!).
Transforming the example using Phantom Types is not hard:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
With phantom types it is not possible to send a cleartext message to
Why not use
type instead and what is the runtime cost?
It’s possible to use a type synonym instead of
newtype. But it makes no sense because the compiler first expand’s type synonyms. This means
Message Cleartext and
Message Encrypted would both be expanded to
String, which defeats the use case of Phantom types.
Guess what: Phantom types comes without runtime costs because there are erased by the compiler at compile time. This means they come for free (at runtime).
There’s another way around
Phantom types are not the only way to achieve this extra information in haskell’s type system. With GADTs it’s also possible to implement the same behavior:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37