RFC: Add HasCallStack constraint to partial Data.List functions.
Henrik Nilsson
Henrik.Nilsson at nottingham.ac.uk
Fri Jun 4 18:09:14 UTC 2021
David Feuer wrote:
> FWIW, I think (!!) tends to show up an awful lot (maybe mostly) in
> code written by beginners who aren't yet thinking of lists
> structurally.
Well, I have no statistical evidence, but I'd disagree.
I, at least, happily use (!!) when I judge it appropriate
given its time complexity and other contextual factors.
In any case, indexing on lists is just one instance.
Many other data structures support indexing operations,
including arrays, and they are all equally partial.
On what grounds should list indexing be singled out?
> foldr1 is deceptive—it's easy to think it can do more than it can,
> and I've seen its laziness characteristics confuse even an
> experienced Haskeller. It's not even really nice for non-empty list
> types. Much clearer:
>
> fr1 :: (a -> b -> b) -> (a -> b) -> NonEmpty a -> b
>
> which is best matched to
>
> data NonEmpty a = End a | Cons a (NonEmpty a)
Fine if one happens to be working with NonEmpty.
But NonEmpty has its own issues, in that it is often the case that
one needs to mix lists with different properties, and formally
keeping track of how these properties interact at the type level
is often not feasible in Haskell, and may well not be worth the
effort even in languages that have much more powerful type systems,
depending on objectives and practical circumstances.
So again, I disagree and maintain that foldr1 is entirely reasonable.
> head and tail are basic ... in Lisp.
I don't think that is a fair statement. There are many data structures
beside list for which head and tail are basic operations. Look
in pretty much any book on data structures and algorithms.
So taking head and tail for lists as a starting point makes perfect
sense from a teaching perspective, for example. (And besides, there is
nothing inherently wrong with Lisp and its descendants, and well-rounded
Computer Scientists and programmers should certainly be aware.)
> In Haskell, they're effectively used ... *checks notes* ... in the
> MonadFix instance for []. Almost every good use of (!!) is for
> something that really should use an infinite list
>
> data Stream a = a :< Stream a
>
> but that uses a list for convenience.
I'd be hesitant to make such sweeping statements myself: a lot depends
on the specifics and practical tradeoffs of individual cases.
But in any case, again, (!!) is only but one example of an indexing
operation. Why should the list one be singled out? The array one
is equally partial.
The fundamental point here is that there is no underlying principle
here, except that undeniably Haskell's more or less "built-in" lists
are used a lot and therefore they of course account for many of
the errors seen.
A further point is that what amounts to operational debugging
annotations really have no place being mixed into types, and
fundamentally undermines the idea of declarative programming.
I guess I am bit of an idealist in this respect, but I still
think there is a lot of mileage in approaching Haskell as
a declarative language, at least until circumstances dictate
that a more operational understanding is needed as a complement.
Best,
/Henrik
foldr1 is deceptive—it's easy to think it can do more than it can, and
I've seen its laziness characteristics confuse even an experienced
Haskeller. It's not even really nice for non-empty list types. Much clearer:
fr1 :: (a -> b -> b) -> (a -> b) -> NonEmpty a -> b
This message and any attachment are intended solely for the addressee
and may contain confidential information. If you have received this
message in error, please contact the sender and delete the email and
attachment.
Any views or opinions expressed by the author of this email do not
necessarily reflect the views of the University of Nottingham. Email
communications with the University of Nottingham may be monitored
where permitted by law.
More information about the Libraries
mailing list