I just looked up the HasResolution
typeclass and it has a single method, resolution
that is declared as follows:
class HasResolution a where
...
resolution :: p a -> Integer
I don't understand the p
in the above declaration. Where does it come from and what does it mean?
It's just a proxy.
If you had
class HasResolution a where
resolution :: Integer
you'd get yelled at because there is no way for the compiler to ever infer which instance of HasResolution
you want when you call resolution
. Specifically, resolution :: HasResolution a => Integer
, where a
appears on the left but not on the right, so you can never infer a
.
So, one solution was
class HasResolution a where
resolution :: a -> Integer
and resolution
's documentation would say that it is not meant to inspect the a
; it is just meant to let the compiler figure out which instance to pick. You'd use it as resolution (undefined :: a)
. Then, another solution emerged:
data Proxy a = Proxy
class HasResolution a where
resolution :: Proxy a -> Integer
Proxy
gives no information to resolution
; it exists, again, only for the compiler to infer what a
is. It's better than the original in that resolution
really can't inspect its argument, so the Integer
is really associated with the type and not with the argument to resolution
. It's slightly worse (or better, depending on who you ask) in that usage is the more verbose resolution (Proxy :: Proxy a)
(you can't just use undefined
because the implementation may pattern match on Proxy
).
This evolved into
class HasResolution a where
resolution :: p a -> Integer
This means that you aren't tied down to just Proxy
, and it means that if you have, e.g., a [a]
lying in scope, you can pass it to resolution
without incurring massive verbosity, while maintaining compatibility with code that just used Proxy
. Again, resolution
's first argument is for the compiler's use only. It means nothing to the actual implementation. You simply use it to select the instance of HasResolution
you want.
Proxy
eventually became so common that GHC.Exts
got a new member: Proxy#
. Proxy#
has no runtime representation, so it incurs no performance penalty, unlike Proxy
(or the above polymorphic p
trick). Proxy#
, however, suffers from the fact that its kind is forall k. k -> TYPE (TupleRep '[])
. By not living in *
like all the other "well-behaved" types, it cannot participate in the polymorphic p
trick.
class HasResolution a where
resolution :: Proxy# a -> Integer
The first solution is rather dated, though you see examples sometimes. The second and third are rather common, and the fourth was superseded pretty quickly by the most recent solution, which is to enable -XTypeApplications -XAllowAmbiguousTypes
and just have
class HasResolution a where
resolution :: Integer
again. -XAllowAmbiguousTypes
staves off the error and -XTypeApplications
lets you specify a
at the call site as resolution @a
. This can't be used in code that needs to be a certain degree of backwards compatible, but you see it more in libraries that require new GHC anyway and can afford to not have compatibility.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With