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?
Accepting a polymorphic argument makes your function rank-2 polymorphic. GHC has an extension for that, but you can only use it if you can somehow quantify over what kinds of types the argument must support – with type constructors or -classes. For instance, for the case of lists you could write
{-# LANGUAGE Rank2Types, UnicodeSyntax #-}
doToBoth_listelem :: (∀ x . [x] -> x) -> [a] -> [b] -> (a,b)
doToBoth_listelem f x y = (f x, f y)
> doToBoth_listelem head [1,10,100] "apple"
(1,'a')
This would also work for the pred
example, and is somewhat more useful. In this case you need to quantify over an argument that's constrained by the Enum
class:
doToBoth_enum :: (Enum a, Enum b)
=> (∀ x . Enum x => x -> x) -> a -> b -> (a,b)
doToBoth_enum f x y = (f x, f y)
> doToBoth_enum pred 2 'b'
(1,'a')
Writing it so it automatically works for any such constraint that the argument might require is, I think, not possible. It might be possible to approximate that with some clever type families and constraint kinds, but I doubt it would end up being practically usable.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With