Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Switch order of arguments for instance declaration in Haskell

Tags:

haskell

I want to make an instance declaration, but the free type variable is not the last variable. For example, I have a class declaration

class Poppable m where
  tryPop :: m a -> Maybe (a, m a)

Now I want to make Q.PSQ (priority queue) an instance of Poppable. Specifically I want something like this:

instance (Ord p) => Poppable (\a -> Q.PSQ a p) where
  tryPop = fmap (first Q.key) . Q.minView

However, this is not legal Haskell code. If the order of arguments to PSQ were switched, then I would have no problem:

instance (Ord p) => Poppable (Q.PSQ p) where
  tryPop = fmap (first Q.key) . Q.minView

How do I switch the order of arguments for the instance declaration?

Now I could wrap PSQ with a newtype:

newtype PSQ'' a b = PSQ'' (Q.PSQ b a)

However this seems clunky to me because I have to constantly wrap/unwrap it. Is there an easier way?

*

I tried using data/type families, but both give errors.

(1) Using a data family declaration:

data family PSQ' a b
data instance PSQ' a b = PSQ b a
instance (Ord p) => Poppable (PSQ' p) where
  tryPop = fmap (first Q.key) . Q.minView

However this gives the error

Couldn't match type `Q.PSQ a p0' with `PSQ' p a'

even though they can match by setting p=p0.

(2) Type families won't work either.

type family PSQ' a b where
  PSQ' b a = Q.PSQ a b

gives

Illegal type synonym family application in instance: PSQ' p
like image 299
holdenlee Avatar asked Dec 24 '22 13:12

holdenlee


1 Answers

Now I could wrap PSQ with a newtype:

newtype PSQ'' a b = PSQ'' (Q.PSQ b a)

However this seems clunky to me because I have to constantly wrap/unwrap it. Is there an easier way?

Nope, not really. You could, of course, write your Poppable class to make it match PSQ. And if you like, you can generalize your newtype to

newtype Flip f a b = Flip (f b a)

at which point you could write

instance Poppable (Flip Q.PSQ a)

but none of this will get rid of the underlying annoyance factor. There are reasons Haskell doesn't support this (apparently, it makes inference much harder, sometimes impossible, etc.), so you'll just have to deal with it.

P.S., that type synonym may be more useful poly-kinded, with {-# LANGUAGE PolyKinds #-}, where it ends up meaning

newtype Flip (f :: k1 -> k2 -> *) (a :: k2) (b :: k1) = Flip (f b a)
like image 132
dfeuer Avatar answered Feb 02 '23 00:02

dfeuer