Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you write rewrite rules for typeclass methods?

Mind the following class:

class ListIsomorphic l where
    toList    :: l a -> [a]
    fromList  :: [a] -> l a

I also demand that toList . fromList == id. How do I write rewrite rules to tell GHC to make that substitution?

like image 690
MaiaVictor Avatar asked Aug 20 '15 23:08

MaiaVictor


1 Answers

You can use a RULES pragma to implement this simplification but you have to do a bit of extra work to make sure the generic method rewrite rules don't fire before yours have a chance to:

{-# RULES
  "protect toList"   toList = toList';
  "protect fromList" fromList = fromList';
  "fromList/toList"  forall x . fromList' (toList' x) = x; #-}

{-# NOINLINE [0] fromList' #-}
fromList' :: (ListIsomorphic l) => [a] -> l a
fromList' = fromList

{-# NOINLINE [0] toList' #-}
toList' :: (ListIsomorphic l) => l a -> [a]
toList' = toList

Here's a silly example to show that it works:

instance ListIsomorphic Maybe where
    toList = error "toList"
    fromList = error "fromList"

test1 :: Maybe a -> Maybe a
test1 x = fromList (toList x)

main = print $ test1 $ Just "Hello"

This prints Just "Hello" instead of erroring out. Also, you can see the rules firing:

$ ghc -O -ddump-rule-firings --make rewrite-method.hs
[1 of 1] Compiling Main             ( rewrite-method.hs, rewrite-method.o )
Rule fired: protect toList
Rule fired: protect fromList
Rule fired: unpack
Rule fired: unpack
Rule fired: protect toList
Rule fired: protect fromList
Rule fired: fromList/toList
Rule fired: unpack
Rule fired: Class op show
Rule fired: >#
Rule fired: tagToEnum#
Rule fired: Class op showsPrec
Rule fired: Class op showList
Rule fired: ++
Rule fired: unpack-list
Rule fired: foldr/app
Rule fired: unpack-list
Rule fired: unpack-list
Linking rewrite-method.exe ...
like image 177
Cactus Avatar answered Nov 06 '22 03:11

Cactus