2533: Generic functions that take integral arguments should work the same way as their prelude counterparts

http://hackage.haskell.org/trac/ghc/ticket/2533 Deadline: Sept 7 The Prelude functions drop, take, and splitAt are unfailing (never call error). This patch changes the Data.List generic versions to behave the same way. At present, they call error on negative arguments. Jim

BTW, where can I put tests for Data.List functions?
http://darcs.haskell.org/testsuite/tests/libraries/Data/test_List.hs
has not been updated in almost two years, and includes only two good tests.
Jim
On Thu, Aug 21, 2008 at 7:47 PM, Jim Apple
http://hackage.haskell.org/trac/ghc/ticket/2533
Deadline: Sept 7
The Prelude functions drop, take, and splitAt are unfailing (never call error). This patch changes the Data.List generic versions to behave the same way. At present, they call error on negative arguments.
Jim

On Thu, Aug 21, 2008 at 08:31:10PM -0700, Jim Apple wrote:
BTW, where can I put tests for Data.List functions?
http://darcs.haskell.org/testsuite/tests/libraries/Data/test_List.hs
has not been updated in almost two years, and includes only two good tests.
Despite the name, the best place is tests/ghc-regress/lib/Data.List/ in the testsuite repo. Thanks Ian

Hi
The Prelude functions drop, take, and splitAt are unfailing (never call error). This patch changes the Data.List generic versions to behave the same way. At present, they call error on negative arguments.
I had always just assumed that take and genericTake did the same thing, so had never even realised this problem existed. I'd call this a bug, that needs fixing. (+1) Neil

On Fri, 2008-08-22 at 11:46 +0100, Neil Mitchell wrote:
Hi
The Prelude functions drop, take, and splitAt are unfailing (never call error). This patch changes the Data.List generic versions to behave the same way. At present, they call error on negative arguments.
I had always just assumed that take and genericTake did the same thing, so had never even realised this problem existed. I'd call this a bug, that needs fixing.
I'd like to know the rationale for the difference in the first place, or hear from one of the spec authors that it was just an oversight. Duncan

On Fri, 22 Aug 2008, Neil Mitchell wrote:
Hi
The Prelude functions drop, take, and splitAt are unfailing (never call error). This patch changes the Data.List generic versions to behave the same way. At present, they call error on negative arguments.
I had always just assumed that take and genericTake did the same thing, so had never even realised this problem existed. I'd call this a bug, that needs fixing.
Maybe the bug is in 'drop', 'take' and 'splitAt' and it was intended to fix it in 'generic' variants. Is there a good reason why to ignore negative number arguments? It may hide bugs.

On Fri, Aug 22, 2008 at 8:49 AM, Henning Thielemann
On Fri, 22 Aug 2008, Neil Mitchell wrote:
Hi
The Prelude functions drop, take, and splitAt are unfailing (never call error). This patch changes the Data.List generic versions to behave the same way. At present, they call error on negative arguments.
I had always just assumed that take and genericTake did the same thing, so had never even realised this problem existed. I'd call this a bug, that needs fixing.
Maybe the bug is in 'drop', 'take' and 'splitAt' and it was intended to fix it in 'generic' variants. Is there a good reason why to ignore negative number arguments? It may hide bugs. _______________________________________________ Libraries mailing list Libraries@haskell.org http://www.haskell.org/mailman/listinfo/libraries
I agree (although the Report doesn't). There's no reason for those functions to be called with negative arguments; all it will do is hide bugs. Alex

Hi
I had always just assumed that take and genericTake did the same thing, so had never even realised this problem existed. I'd call this a bug, that needs fixing.
Maybe the bug is in 'drop', 'take' and 'splitAt' and it was intended to fix it in 'generic' variants. Is there a good reason why to ignore negative number arguments? It may hide bugs.
Too late. There is code depending on this behaviour in the wild, and we can't break it without a really really really good reason. Changing undefined to a value is not too bad (some optimisations in ByteString do it automatically even!), but changing a value to undefined is bad. Thanks Neil

Neil Mitchell
Hi
The Prelude functions drop, take, and splitAt are unfailing (never call error). This patch changes the Data.List generic versions to behave the same way. At present, they call error on negative arguments.
I had always just assumed that take and genericTake did the same thing, so had never even realised this problem existed. I'd call this a bug, that needs fixing.
Maybe the bug is in 'drop', 'take' and 'splitAt' and it was intended to fix it in 'generic' variants. Is there a good reason why to ignore negative number arguments? It may hide bugs.
A similar argument could be made against ``take 5 [] = []''. A different solution would be using Nat or Natural as arguments here --- then the conversion introduces an obvious place to check for errors. Wolfram

On 2008.08.23 17:33:55 -0000, kahl@cas.mcmaster.ca scribbled 0.9K characters:
Neil Mitchell
wrote: Hi
The Prelude functions drop, take, and splitAt are unfailing (never call error). This patch changes the Data.List generic versions to behave the same way. At present, they call error on negative arguments.
I had always just assumed that take and genericTake did the same thing, so had never even realised this problem existed. I'd call this a bug, that needs fixing.
Maybe the bug is in 'drop', 'take' and 'splitAt' and it was intended to fix it in 'generic' variants. Is there a good reason why to ignore negative number arguments? It may hide bugs.
A similar argument could be made against ``take 5 [] = []''.
A different solution would be using Nat or Natural as arguments here --- then the conversion introduces an obvious place to check for errors.
Wolfram
I've actually long wondered about this: why don't more functions use Nat where it'd make sense? It can't be because Nat is hard to define - I'd swear I've seen many definitions of Nat (if not dozens when you count all the type-level exercises which include one). -- gwern ISSSP SADT NSV Rachel HAMASMOIS & Lindows wire ASPIC clandestine

On 2008 Aug 23, at 15:46, Gwern Branwen wrote:
I've actually long wondered about this: why don't more functions use Nat where it'd make sense? It can't be because Nat is hard to define - I'd swear I've seen many definitions of Nat (if not dozens when you count all the type-level exercises which include one).
Because naive definitions are dog-slow and fast definitions are anything but easy to use? -- brandon s. allbery [solaris,freebsd,perl,pugs,haskell] allbery@kf8nh.com system administrator [openafs,heimdal,too many hats] allbery@ece.cmu.edu electrical and computer engineering, carnegie mellon university KF8NH

Brandon S. Allbery wrote:
Gwern Branwen wrote:
I've actually long wondered about this: why don't more functions use Nat where it'd make sense? It can't be because Nat is hard to define - I'd swear I've seen many definitions of Nat (if not dozens when you count all the type-level exercises which include one).
Because naive definitions are dog-slow and fast definitions are anything but easy to use?
Can you examples of both naive definitions and fast definitions of Nat? I'm curious. Sean

Can you examples of both naive definitions and fast definitions of Nat? I'm curious.
Naive:
data Natural' = Zero | Succ Natural'
Fast:
type Nat = Word64
(or Word if you want to retrofit to Haskell-1.5, a.k.a. Haskell98 ;-).
newtype Natural = N Integer -- to be exported abstractly
instance Num Natural where ... N a - N b = let d = a - b in if d >= 0 then N d else error "Illegal Natural subtraction" -- one argument against Num ;-)
subtract (N a) (N b) = let d = a - b in if d >= 0 then Just $ N d else Nothing
.... Wolfram

Sean Leather wrote:
Can you examples of both naive definitions and fast definitions of Nat? I'm curious.
Besides naive or fast, there is also lazy. So for example, using lazy naturals, the expression genericLength x < genericLength y only forces enough of x and y to determine which is longer. Here is John Meacham's implementation: http://www.haskell.org/pipermail/haskell-cafe/2007-October/033213.html Regards, Yitz

On 2008.08.23 15:48:10 -0400, "Brandon S. Allbery KF8NH"
On 2008 Aug 23, at 15:46, Gwern Branwen wrote:
I've actually long wondered about this: why don't more functions use Nat where it'd make sense? It can't be because Nat is hard to define - I'd swear I've seen many definitions of Nat (if not dozens when you count all the type-level exercises which include one).
Because naive definitions are dog-slow and fast definitions are anything but easy to use?
-- brandon s. allbery [solaris,freebsd,perl,pugs,haskell] allbery@kf8nh.com
While I am but a mediocre Haskell programmer at best, I can't say I find that a satisfying explanation. When I read the GHC & fusion papers (among many many other fine papers relating to Haskell), I am impressed at the optimizations the authors managed to eek out despite the difficult conditions they labor under. With that in mind, I find it hard to accept that there is no approach which is fast and easy to use - no theorems or rewrite rules or library which provides it. When I consider it, it seems to me that it's not hard to define a Nat type which does checks for < 0 at runtime, so given the Olegian feats Haskell lends itself to, why is it not easy to staticly turn Nats into Ints or similarly performant types? -- gwern CESID Security Delta d DUVDEVAN SRI composition data-haven SONANGOL World

On 2008 Aug 24, at 1:06, Gwern Branwen wrote:
scribbled 0.5K characters: On 2008 Aug 23, at 15:46, Gwern Branwen wrote:
I've actually long wondered about this: why don't more functions use Nat where it'd make sense? It can't be because Nat is hard to define - I'd swear I've seen many definitions of Nat (if not dozens when you count all the type-level exercises which include one).
Because naive definitions are dog-slow and fast definitions are anything but easy to use? While I am but a mediocre Haskell programmer at best, I can't say I find that a satisfying explanation. When I read the GHC & fusion
On 2008.08.23 15:48:10 -0400, "Brandon S. Allbery KF8NH"
There are fast ones that are easy to use; they use Template Haskell, which isn't H98. As for why: it's not so much that its hard to optimize, it's that Haskell doesn't lend itself *conveniently* to type-level programming (which this is), so you end up resorting to TH or having rather ugly types all over the place. (Optimizing the naive case... you'd have to ask the GHC folks, but I suspect while they could optimize for it, it'd be a sufficiently narrow optimization that they'd want to see enough use to justify it.) -- brandon s. allbery [solaris,freebsd,perl,pugs,haskell] allbery@kf8nh.com system administrator [openafs,heimdal,too many hats] allbery@ece.cmu.edu electrical and computer engineering, carnegie mellon university KF8NH

| >> I've actually long wondered about this: why don't more functions use | >> Nat where it'd make sense? It can't be because Nat is hard to define - | >> I'd swear I've seen many definitions of Nat (if not dozens when you | >> count all the type-level exercises which include one). | > | > Because naive definitions are dog-slow and fast definitions are anything | > but easy to use? I doubt that even GHC is going to optimise data Nat = Z | S Nat into data Nat = N Int# (with appropriate checks) anytime soon. I think the main reason that the latter (which can easily be implemented as a library) is not more widely used is that it's tiresomely incompatible with functions that produce Ints. Also perhaps if length :: [a] -> Nat, then computing the difference between two lengths (length xs - length ys) could produce a runtime error. But these are all matters of taste, software engineering, and inertia (legacy code). It'd be a worthwhile exercise to try making a version of the standard libs using Nat where it's appropriate, and see how convenient or otherwise it turns out to be. Simon

On 2008-08-26, Simon Peyton-Jones
| >> I've actually long wondered about this: why don't more functions use | >> Nat where it'd make sense? It can't be because Nat is hard to define - | >> I'd swear I've seen many definitions of Nat (if not dozens when you | >> count all the type-level exercises which include one). | > | > Because naive definitions are dog-slow and fast definitions are anything | > but easy to use?
I doubt that even GHC is going to optimise data Nat = Z | S Nat into data Nat = N Int# (with appropriate checks) anytime soon.
I think the main reason that the latter (which can easily be implemented as a library) is not more widely used is that it's tiresomely incompatible with functions that produce Ints.
(Or Integers, or Integral a => a). Definitely.
Also perhaps if length :: [a] -> Nat, then computing the difference between two lengths (length xs - length ys) could produce a runtime error.
It's not so crazy to think that the right thing to do for the subtraction of naturals overflowing is return 0. It fits in with drop and takes behaviour, and arguably zips. There are natural near homomorphisms in place between lists and numbers, and taking negatives as 0 seems to improve the matching a bit. -- Aaron Denney -><-

On Sat, 23 Aug 2008, Gwern Branwen wrote:
A similar argument could be made against ``take 5 [] = []''.
sure
A different solution would be using Nat or Natural as arguments here --- then the conversion introduces an obvious place to check for errors.
Wolfram
I've actually long wondered about this: why don't more functions use Nat where it'd make sense? It can't be because Nat is hard to define - I'd swear I've seen many definitions of Nat (if not dozens when you count all the type-level exercises which include one).
http://hackage.haskell.org/cgi-bin/hackage-scripts/package/non-negative

On Fri, Aug 22, 2008 at 05:49:22PM +0200, Henning Thielemann wrote:
The Prelude functions drop, take, and splitAt are unfailing (never call error). This patch changes the Data.List generic versions to behave the same way. At present, they call error on negative arguments.
I had always just assumed that take and genericTake did the same thing, so had never even realised this problem existed. I'd call this a bug, that needs fixing.
Maybe the bug is in 'drop', 'take' and 'splitAt' and it was intended to fix it in 'generic' variants. Is there a good reason why to ignore negative number arguments? It may hide bugs.
But is also makes the functions less useful and another source of non-completeness. I certainly always considered accepting negative numbers a feature of those functions and not an infelicity. Sometimes you want 'drop 1 xs' and other times you want 'tail xs' (or equivalent). John -- John Meacham - ⑆repetae.net⑆john⑈

On Tue, 26 Aug 2008, John Meacham wrote:
But is also makes the functions less useful and another source of non-completeness. I certainly always considered accepting negative numbers a feature of those functions and not an infelicity. Sometimes you want 'drop 1 xs' and other times you want 'tail xs' (or equivalent).
It's very confusing for readers of your programs, if you use 'drop 1' instead of 'tail'. The names 'drop' and 'tail' don't give the reader a hint, that 'drop' works for empty lists and 'tail' doesn't. 'drop 1' and 'tail' should behave identically for empty lists and a function with different behaviour should have a different name.

On Wed, 2008-08-27 at 09:19 +0200, Henning Thielemann wrote:
On Tue, 26 Aug 2008, John Meacham wrote:
But is also makes the functions less useful and another source of non-completeness. I certainly always considered accepting negative numbers a feature of those functions and not an infelicity. Sometimes you want 'drop 1 xs' and other times you want 'tail xs' (or equivalent).
It's very confusing for readers of your programs, if you use 'drop 1' instead of 'tail'. The names 'drop' and 'tail' don't give the reader a hint, that 'drop' works for empty lists and 'tail' doesn't.
I doubt very much that the name `drop' means anything until you learn it. In particular, it's meaningless for native speakers of Chinese who haven't learned English, as well. And it's only slightly less meaningless (when you consider what it does) for native speakers of English. jcc

On 2008 Aug 27, at 11:58, Jonathan Cast wrote:
On Wed, 2008-08-27 at 09:19 +0200, Henning Thielemann wrote:
On Tue, 26 Aug 2008, John Meacham wrote:
But is also makes the functions less useful and another source of non-completeness. I certainly always considered accepting negative numbers a feature of those functions and not an infelicity. Sometimes you want 'drop 1 xs' and other times you want 'tail xs' (or equivalent).
It's very confusing for readers of your programs, if you use 'drop 1' instead of 'tail'. The names 'drop' and 'tail' don't give the reader a hint, that 'drop' works for empty lists and 'tail' doesn't.
I doubt very much that the name `drop' means anything until you learn it. In particular, it's meaningless for native speakers of Chinese who haven't learned English, as well. And it's only slightly less meaningless (when you consider what it does) for native speakers of English.
And for that matter, even to me (an experienced programmer) my first (admittedly not entirely off base, just mostly) thought is the Forth/ PostScript "drop". -- brandon s. allbery [solaris,freebsd,perl,pugs,haskell] allbery@kf8nh.com system administrator [openafs,heimdal,too many hats] allbery@ece.cmu.edu electrical and computer engineering, carnegie mellon university KF8NH

On Wed, Aug 27, 2008 at 3:19 AM, Henning Thielemann
On Tue, 26 Aug 2008, John Meacham wrote:
But is also makes the functions less useful and another source of non-completeness. I certainly always considered accepting negative numbers a feature of those functions and not an infelicity. Sometimes you want 'drop 1 xs' and other times you want 'tail xs' (or equivalent).
It's very confusing for readers of your programs, if you use 'drop 1' instead of 'tail'. The names 'drop' and 'tail' don't give the reader a hint, that 'drop' works for empty lists and 'tail' doesn't. 'drop 1' and 'tail' should behave identically for empty lists and a function with different behaviour should have a different name.
Personally, I'd prefer to see tail dropped from the Prelude (not any time soon, of course). The fewer incomplete functions are in the Prelude, the better, and drop has very nice and easy-to-understand behavior. Folks who want to crash on empty lists should write their own or use pattern matching. David

On 2008-08-27, David Roundy
On Wed, Aug 27, 2008 at 3:19 AM, Henning Thielemann
wrote: It's very confusing for readers of your programs, if you use 'drop 1' instead of 'tail'. The names 'drop' and 'tail' don't give the reader a hint, that 'drop' works for empty lists and 'tail' doesn't. 'drop 1' and 'tail' should behave identically for empty lists and a function with different behaviour should have a different name.
Personally, I'd prefer to see tail dropped from the Prelude (not any time soon, of course). The fewer incomplete functions are in the Prelude, the better, and drop has very nice and easy-to-understand behavior. Folks who want to crash on empty lists should write their own or use pattern matching.
Ditto. It's one of the more noticeable and easily avoidable sources of crashes in my code. I'm not being serious, but of course, it could be defined using fail, which defaults to [] in the List monad... -- Aaron Denney -><-

On Thu, Aug 21, 2008 at 07:47:03PM -0700, Jim Apple wrote:
The Prelude functions drop, take, and splitAt are unfailing (never call error). This patch changes the Data.List generic versions to behave the same way. At present, they call error on negative arguments.
Looks like a bug/inconsistency in H98 to me: http://haskell.org/onlinereport/list.html#sect17.7 says The prefix "generic" indicates an overloaded function that is a generalised version of a Prelude function. so I'd certainly expect the generic versions to behave the same if used at type ...Int... Malcolm, do you agree? Can this go in the H98 errata? Thanks Ian
participants (16)
-
Aaron Denney
-
Alexander Dunlap
-
Brandon S. Allbery KF8NH
-
David Roundy
-
Duncan Coutts
-
Gwern Branwen
-
Henning Thielemann
-
Ian Lynagh
-
Jim Apple
-
John Meacham
-
Jonathan Cast
-
kahl@cas.mcmaster.ca
-
Neil Mitchell
-
Sean Leather
-
Simon Peyton-Jones
-
Yitzchak Gale