First, an aside. In playing with this, I ran into something odd. Consider
the functions:
f :: (a -> c) -> a -> b -> c
f g a b = g a
f :: (b -> c) -> a -> b -> c
f g a b = g b
These are clearly potentially problematic, but they are not caught as
ambiguous, even for "f id 1 2". Why not?
Back to my point: for the purpose of overloading, the return type doesn't
actually matter. In fact, we may write "a -> b" to mean we just don't care to
figure out the return type at this point, but we want to overload for any
argument type.
But, actually other contravariant types don't matter either. You may wish to
say:
f :: (x -> x) -> a -> a
Meaning, your function can take any (identity) function. (ghc has no problem
with this, while on the other hand it complains about "a -> b".) We
complain that you're trying to overload on the contravariant x. What if you
just want to say this f overload applies whenever the first argument is a
function?
That is, maybe you want something like:
overload f :: (_ -> _) -> _
type f :: (x -> x) -> a -> a
Now, when we discussed this before I think we said you could always put a more
specific type signature with the definition, so that takes care of the second
line. The issue is how to deal with the first.
What if we literally allow "_" (or "*") in overload specifications to mean
"some type goes here, but it's not relevant to the overload." Does that make
sense? Semantically it's just like a new type variable, but doesn't count
against checkLeftovers.
That definitely seems like a bug. Both functions match, and neither
one is a specialization of the other.
> Back to my point: for the purpose of overloading, the return type doesn't
> actually matter. In fact, we may write "a -> b" to mean we just don't care to
> figure out the return type at this point, but we want to overload for any
> argument type.
>
> But, actually other contravariant types don't matter either. You may wish to
> say:
>
> f :: (x -> x) -> a -> a
>
> Meaning, your function can take any (identity) function. (ghc has no problem
> with this, while on the other hand it complains about "a -> b".) We
> complain that you're trying to overload on the contravariant x. What if you
> just want to say this f overload applies whenever the first argument is a
> function?
>
> That is, maybe you want something like:
>
> overload f :: (_ -> _) -> _
> type f :: (x -> x) -> a -> a
>
> Now, when we discussed this before I think we said you could always put a more
> specific type signature with the definition, so that takes care of the second
> line. The issue is how to deal with the first.
>
> What if we literally allow "_" (or "*") in overload specifications to mean
> "some type goes here, but it's not relevant to the overload." Does that make
> sense? Semantically it's just like a new type variable, but doesn't count
> against checkLeftovers.
That seems perfectly reasonable. Easy to understand and simple to implement.
Geoffrey