Skip to content

Implement NonEmpty as a newtype

At MuiHac today I had a discussion about NonEmpty, and we wondered if it would be beneficial to implement NonEmpty as

newtype NonEmpty a = NonEmpty [a]

with :| being a pattern synonym (a COMPLETE and associated one, so the change would be invisible to users), and NonEmpty not being exported, of course, to preserve the invariant in the Data.List.NonEmpty module.

The benefits would be less code (a few functions can be just coerce’d versions of the normal list functions), and allocation-free conversion to and from lists. And it would show off the ability to use pattern synonyms to transparently change the implementation.

I briefly had a look if that’s possible, but it’s not as simple as I thought:

The type is defined in GHC.Base, not in Data.List.NonEmpty, so we couldn’t actually hide the NonEmpty constructor completely, making this change quite a bit less appealing. But it’s in GHC.Base for a good reason (it’s used in the definition of the Semigroup class). I don’t see a good way around that (without using .hs-boot files, at least).

I’m writing this issue in the hope it can be found if someone else wonders “why is NonEmpty not a newtype”.

It also points to a deeper problems in Haskell: water-tight abstraction with newtypes with hidden constructors is only possible if all “priviledged” code is within a single module; I can’t easily safely allow some other modules to access the constructors, but not all.

To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information