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.