The problem with your signature is f . Let's decompose it a bit:
mapAndZip3 :: forall (a :: *) (b :: *) (c :: *) (f :: *->*) => (forall x. x -> fx) -> [a] -> [b] -> [c] -> [(fa, fb, fc)]
f here is supposed to be "any level level function", in your instance it will be type f (a,b) = a . But Haskell does not allow you to abstract from type level functions, only over type constructors such as Maybe or IO . Thus, mapAndZip3 Just really will be possible, but fst does not create, but deconstructs the type of the tuple.
Type functions do not actually exist in Haskell98, they are only possible with TypeFamilies . The problem is mainly that the Haskell native language is untyped 1, but type level functions must be full 2 functions. But then you cannot define any non-trivial function (i.e., except id or type constructors) that are defined for all types. The type-type fst , of course, is not complete; it only works with tuple types.
So, to work with such functions, you obviously need to specify their context in some other way. With TypeFamilies it can work as follows:
class TypeFunctionDomain d where type TypeFunction d :: * instance TypeFunctionDomain (a,b) where type TypeFunction (a,b) = a mapAndZip3 :: (forall x. TypeFunctionDomain x => x -> TypeFunction x) -> [a] -> [b] -> [c] -> [(TypeFunction a, TypeFunction b, TypeFunction c)]
However, this is not quite what you want: it would be impossible to define an instance of TypeFunctionDomain in the same TypeFunctionDomain that is responsible for snd , which means that mapAndZip3 will not actually be polymorphic at all, but they work with only one function.
These problems can only be solved in type-dependent languages such as Agda , where views are really just types of types, and you can define type level functions as well as value level functions. But it’s like a price: all functions must be full functions! This is not very bad, but it means that these languages are not really Turing at all (which will require the possibility of an infinite loop / recursion, but, nevertheless, ⟂ regarding a complete evaluation of the result).
1 Things have changed a bit with the appearance of a kind of polymorphism, etc. .
2 . Unlike, for example, C ++, which allows - albeit with a very terrible syntax - to perform functions of the type at the duck level using templates. This may be a pretty nice feature, but one of the consequences is that you often get completely unreadable error messages (with even less relevance to the real problem than the worst “GHC” “Possible fixes”) when you try to create a template using an argument of type outside the implicit domain.