Consider this function:
doToBoth f x y = (f x, f y)
It works as expected in simple cases:
doToBoth (2 *) 10 15 == (20, 30)
doToBoth head [1,2] [3,4,5] == (1, 3)
Then I tried these ones:
doToBoth head [1,10,100] "apple"
doToBoth pred 2 'b'
I want those to both result in (1, 'a'), but instead they just result in a type error. The problem is that the inferred type of doToBoth isn't polymorphic enough:
doToBoth :: (a1 -> a2) -> a1 -> a1 -> (a2, a2)
Seeing this, I tried adding an explicit type signature to fix it:
doToBoth :: (t ~ (i1 -> o1), t ~ (i2 -> o2)) => t -> i1 -> i2 -> (o1, o2)
This type signature was accepted, but it didn't fix the problem, and checking what happened with :t doToBoth revealed that it ended up with a type equivalent to the original inferred one:
doToBoth :: (i2 -> o2) -> i2 -> i2 -> (o2, o2)
What is the correct way to write a type signature to make this function work the way I want?