-
Notifications
You must be signed in to change notification settings - Fork 18
Description
Let's extend Data.List / Data.List.NonEmpty with new functions:
compareLength :: [a] -> Int -> Ordering
compareLength xs n
| n < 0 = GT
| otherwise = foldr
(\_ f m -> if m > 0 then f (m - 1) else GT)
(\m -> if m > 0 then LT else EQ)
xs
n
compareLength :: NonEmpty a -> Int -> Ordering
compareLength xs n
| n < 1 = GT
| otherwise = foldr
(\_ f m -> if m > 0 then f (m - 1) else GT)
(\m -> if m > 0 then LT else EQ)
xs
nThe idea is that compareLength xs n serves as a safer and faster alternative to compare (length xs) n. Similarly, it's better to write compareLength xs 10 == LT instead of length xs < 10. While length would force and traverse the entire spine of xs (which could even diverge if xs is infinite), compareLength traverses at most n elements to determine its result.
(This deficiency of length has been long recognised by warning STAN-0103 in stan)
> compareLength [] 0
EQ
> compareLength [] 1
LT
> compareLength [`a`] 1
EQ
> compareLength [`a`, `b`] 1
GT
> compareLength [0..] 100
GT
> compareLength undefined (-1)
GT
> compareLength ('a' : undefined) 0
GT
Prior art:
The choice of name compareLength follows precedents in prior art, but also it's most natural to name a better version of (compare .) . length as compareLength.
MR can be found at https://gitlab.haskell.org/ghc/ghc/-/merge_requests/12822
-
How about generalizing from
[a]andNonEmpty ato anyFoldable?While the definition above uses only
foldrand indeed works for anyFoldable, it's hard to tell whethercompareLengthis better than(compare .) . lengthfor a givenFoldableor no. Iflengthis O(1) thencompare (length xs) nis faster thancompareLength xs nwhich is O(n). Thus the proposal argues for addingcompareLengthonly to containers for whichlengthis known to be inefficient.Having the monomorphic
Data.List.compareLengthdoes not preclude anyone from raising a proposal to addData.Foldable.compareLengthor whatever they fancy to argue for. -
How about generalizing from
Intto anyNum?I imagine that in the majority of cases
nincompareLength xs nis a literal constant, so the change would force everyone to write an explicit type as incompareLength xs (10 :: Int), which will get very annoying soon. So let's stick to the usual convention ofIntfor length/indexing inData.List.One could argue for
genericlengthCompare xs n, polymorphic bynsimilar to the existinggenericLength, but again, let me leave it to others to propose. -
How about adding a rewrite rule changing
compare (length xs) ntocompareLength xs n?Such rule could affect semantics, making diverging programs terminate. While I'd expect such change to be a desirable one in the majority of cases, I'd rather leave it to a (potential) subsequent proposal.