As an example, let's take the following algorithm for computing the longest common subsequence of two sequences, copied from Rosetta Code:
longest xs ys = if length xs > length ys then xs else ys
lcs [] _ = []
lcs _ [] = []
lcs (x:xs) (y:ys)
| x == y = x : lcs xs ys
| otherwise = longest (lcs (x:xs) ys) (lcs xs (y:ys))
lcs
impicitly assumes that both arguments are of type Eq a => [a]
; indeed, if I try to explicitly give the signature lcs :: [a] -> [a] -> [a]
an error occurs on the line where x == y
is, whereas the signature lcs :: Eq a => [a] -> [a] -> [a]
works.
Now let's suppose I have two lists l1
and l2
, both of type [(a,b)]
, and that I want the LCS between them, but in a way that uses the ==
operator in the definition of lcs
only between the snd
s of each element (obviously b
is required to belong to Eq
typeclass).
I could provide lcs
with not only the two lists, but also with the equality operator, which in the specific example above would be (==) `on` snd
. The signature of lcs
would then be (a -> a -> Bool) -> [a] -> [a] -> [a]
.
However, this would force the user to provide the equality operator even in the trivial case that one wants to use a plain (==)
, and wrapping the (a -> a)
argument in a Maybe
wouldn't help either.
In other words I feel that in the general case the constraint on the argument via Eq a =>
is ok, but in other cases one might want to pass a custom equality operator removing that constraint, which makes me thing of function overloading.
How should I go about this?
You simply provide both – a custom-operator version lcsBy
, and an Eq a =>
version which is implemented in terms of that.
lcsBy :: (a->a->Bool) -> [a] -> [a] -> [a]
...
lcsBy compOp (x:xs) (y:ys)
| compOp x y = ...
...
lcs :: Eq a => [a] -> [a] -> [a]
lcs = lcsBy (==)
This is analogous to how the base library provides both maximum
and maximumBy
.