One common oversight in Haskell compilers is failing to intern identifiers using Ints and failing to prefer IntMaps and IntSets. The PureScript compiler, for instance, uses Maps as of writing.

I always do the following:

newtype Unique = Unique { unUnique :: Int }

data Name = Name { name   :: T.Text
                 , unique :: !Unique
                 ...
                 }

instance Eq (Name a) where
    (==) (Name \_ u) (Name \_ u') = u == u'

instance Ord (Name a) where
    compare (Name \_ u) (Name \_ u') = compare u u'

Note that making the name field lazy makes things faster.

Using Uniques makes equality and comparisons faster; moreover, one can use Names as keys in IntMaps and IntSets, which have more efficient implementations. I came across the dramatic performance advantage in my Kempe compiler; liveness analysis is drastically faster with IntSets compared to Sets.