Finding ways to fake this sort of thing using overwrought type system tricks is one of my hobbies, so trust me when I say that the result is pretty ugly. In particular, note that tuples aren't defined recursively, so there's no real way to abstract over them directly; as far as Haskell's type system is concerned, every tuple size is completely distinct.
Any viable approach for working with tuples directly will therefore require code generation--either using TH, or an external tool as with the tuple
package.
To fake it without using generated code, you have to first resort to using recursive definitions--typically right-nested pairs with a "nil" value to mark the end, either (,)
and ()
or something equivalent to them. You may notice that this is similar to the definition of lists in terms of (:)
and []
--and in fact, recursively defined faux-tuples of this sort can be seen as either type-level data structures (a list of types) or as heterogeneous lists (e.g., HList works this way).
The downsides include, but are not limited to, the fact that actually using things built this way can be more awkward than it's worth, the code to implement the type system tricks is usually baffling and completely non-portable, and the end result is not necessarily equivalent anyway--there are multiple nontrivial differences between (a, (b, (c, ())))
and (a, b, c)
, for instance.
If you want to see how horrible it becomes you can look at the stuff I have on GitHub, particularly the bits here.