Is there any type-safe way to write a function
bi f a b = (f a, f b)
such that it would be possible to use it like this:
x1 :: (Integer, Char)
x1 = bi head [2,3] "45"
x2 :: (Integer, Char)
x2 = bi fst (2,'3') ('4',5)
x3 :: (Integer, Double)
x3 = bi (1+) 2 3.45
? In rank-n-types examples there are always something much simpler like
g :: (forall a. a -> a) -> a -> a -> (a, a)
g f a b = (f a, f b)
{-# LANGUAGE TemplateHaskell #-}
bi f = [| \a b -> ($f a, $f b)|]
ghci> :set -XTemplateHaskell
ghci> $(bi [|head|]) [2,3] "45"
(2,'4')
;)
Yes, though not in Haskell. But the higher-order polymorphic lambda calculus (aka System F-omega) is more general:
bi : forall m n a b. (forall a. m a -> n a) -> m a -> m b -> (n a, n b)
bi {m} {n} {a} {b} f x y = (f {a} x, f {b} y)
x1 : (Integer, Char)
x1 = bi {\a. List a} {\a. a} {Integer} {Char} head [2,3] "45"
x2 : (Integer, Char)
x2 = bi {\a . exists b. (a, b)} {\a. a} {Integer} {Char} (\{a}. \p. unpack<b,x>=p in fst {a} {b} x) (pack<Char, (2,'3')>) (pack<Integer, ('4',5)>)
x3 : (Integer, Double)
x3 = bi {\a. a} {\a. a} {Integer} {Double} (1+) 2 3.45
Here, I write f {T}
for explicit type application and assume a library typed respectively. Something like \a. a
is a type-level lambda. The x2
example is more intricate, because it also needs existential types to locally "forget" the other bit of polymorphism in the arguments.
You can actually simulate this in Haskell by defining a newtype
or datatype for every different m
or n
you instantiate with, and pass appropriately wrapped functions f
that add and remove constructors accordingly. But obviously, that's no fun at all.
Edit: I should point out that this still isn't a fully general solution. For example, I can't see how you could type
swap (x,y) = (y,x)
x4 = bi swap (3, "hi") (True, 3.1)
even in System F-omega. The problem is that the swap
function is more polymorphic than bi
allows, and unlike with x2
, the other polymorphic dimension is not forgotten in the result, so the existential trick does not work. It seems that you would need kind polymorphism to allow that one (so that the argument to bi
can be polymorphic over a varying number of types).
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