3 ms·
NonEmptyList is usually implemented that way to be a bit lazier on the implementation end. You can also write it as data NEL a = One a | More a (NEL a) bu
by tel 13y ago
NonEmptyList is usually implemented that way to be a bit lazier on the implementation end. You can also write it as
data NEL a = One a | More a (NEL a)
but then you have to write fresh functions for all of the default list methods without any performance improvements (which make doing so totally valuable for things like Vector). You also have NEL being an instance of a more general NonEmptyFunctor pattern like in (http://hackage.haskell.org/packages/archive/non-empty/0.1.3/doc/html/Data-NonEmpty.html http://hackage.haskell.org/packages/archive/non-empty/0.1.3/...).
With this recursive NEL, last is
last :: NEL a -> a
last (One a) = a
last (More _ more) = last more
and (inefficient, corecursive) length is
len :: NEL a -> Int
len (One _) = 1
len (More _ more) = succ (len more)
NELs are quite useful not just for safety. head as type [a] -> a is largely considered to be a mistake, the Safe package is quite popular and redefines head as having type [a] -> Maybe a. I use it frequently.
NELs have properties like being Semigroups (and not Monoids) and Comonads which regular lists do not. This helps not only for theoretical guarantees of safety but also so that we can feel confident that our typeclass declarations have the meaning they're supposed to have.
So is (NEL a = NEL a [a]) a bad solution? It's perhaps less independently beautiful than the truly recursive NEL, but it's easier to write and takes advantage of Haskell's built in "possibly empty List" functionality. It's also more "globally beautiful" in that it fits into the family of types "non-empty functors". The recursive version is also trivially isomorphic, so there's no loss in functionality.
- tel 13y ago(quick addendum: by "lazier" I meant simpler, not that either implementation of NEL is denotationally lazier than the other.)