The Haskell 2010 Language Report states in section 20.10.1.1 that:
deleteBy :: (a -> a -> Bool) -> a -> [a] -> [a]
In fact, the implementation in the GHC library would allow
deleteBy :: (b -> a -> Bool) -> b -> [a] -> [a]
but actually restricts the type to the former one with the annotation.
Hence, one cannot say, for instance:
foo = deleteBy fsteq 42 [(43, "foo"), (44, "bar"), (42, "baz")] where
fsteq a (b,_) = a == b
because Int
is not the same as (Int, String)
.
Is there any good reason for this?
The reason I am asking is that, if there is no good reason for it, I would include deleteBy
with the more general type in the Frege port of Data.List I am currently doing. But maybe I am overlooking something?
EDIT: As @hammar pointed out, this applies to other xxxBy functions also.
Generalising the type of deleteBy
violates the standard in a very practical way: perfectly valid Haskell programs become invalid, thanks to unresolved overloading.
Here's a demonstration:
class (Num a) => Magic a where
magic :: a -> Bool
sameMagic :: (Magic a, Magic b) => a -> b -> Bool
sameMagic a b = magic a == magic b
test :: (Magic a) => [a]
test = deleteBy sameMagic 42 [1234]
In Haskell, this program is perfectly well-typed; deleteBy
's restricted type ensures that the 42
is guaranteed to have the same type as the 1234
. With the generalised deleteBy
, this is not the case, and so the type of 42
is ambiguous, making the program invalid. (If you want a less contrived example, consider a function which compares two Integral
values with toInteger
.)
So, perhaps there is no good reason for this restricted type (although if deleteBy
is to be generalised, I would prefer hammar's version to your proposal), but generalising it does violate the standard, and it can break valid programs.