Short note on Data.Proxy based on this Stackoverflow answer.

First, a few imports:

Suppose we want to check if some fuzzy real world data can be read as certain concrete types. We could write a few helper functions using readMaybe:

These are all basically the same. How to generalise? Let’s try a typeclass.

This doesn’t work since readableAs doesn’t depend on the type t:

    The class method ‘readableAs’
mentions none of the type or kind variables of the class ‘ReadableAs t’
When checking the class method: readableAs :: String -> Bool
In the class declaration for ‘ReadableAs’


So put the type in:

This compiles, so let’s write some instances:

Using it is clunky since we have to come up with a concrete value for the first argument:

> readableAs' (0::Int) "0"
True
True


For some types we could use Data.Default for this placeholder value. But for other types nothing will make sense. How do we choose a default value for Foo?

Haskell has non-strict evaluation so we can use undefined, but, ugh. Bad idea.

 > readableAs' (undefined::Int) "0"
True


So let’s try out Proxy. It has a single constructor and a free type variable that we can set:

> :t Proxy
Proxy :: Proxy t

> Proxy :: Proxy Bool
Proxy
> Proxy :: Proxy Int
Proxy
> Proxy :: Proxy Double
Proxy


Let’s use Proxy t instead of t:

This works, and we don’t have to come up with the unused concrete value:

> readableAsP (Proxy :: Proxy Bool) "0"
False
> readableAsP (Proxy :: Proxy Bool) "True"
True
> readableAsP (Proxy :: Proxy Int) "0"
True
> readableAsP (Proxy :: Proxy Double) "0"
True
> readableAsP (Proxy :: Proxy Double) "0.0"
True


Still, there’s a lot of duplication in the class and instances. We can do away with the class entirely. With the ScopedTypeVariables language extension and the forall, the t in the type signature can be referred to in the body:

> readableAs (Proxy :: Proxy Int) "0"
True
> readableAs (Proxy :: Proxy Int) "foo"
False