Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

HasResolution typeclass

Tags:

haskell

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?

like image 749
melston Avatar asked Feb 15 '18 00:02

melston


1 Answers

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.

like image 117
HTNW Avatar answered Oct 20 '22 10:10

HTNW