I was looking at the partial functions Data.List.init
and Data.List.last
and their total namesakes in Data.List.NonEmpty
, and I noticed that the latter were specified as:
-- | Extract the last element of the stream.
last :: NonEmpty a -> a
last ~(a :| as) = List.last (a : as)
-- | Extract everything except the last element of the stream.
init :: NonEmpty a -> [a]
init ~(a :| as) = List.init (a: as)
I wondered what were the respective merits or drawbacks of recursive specifications, such as:
last :: NonEmpty a -> a
last (a :| []) = a
last (_ :| (a : as)) = last (a :| as)
init :: NonEmpty a -> [a]
init (_ :| []) = []
init (a1 :| (a2 : as)) = a1 : init (a2 :| as)