In chapter 2 of "A gentle introduction to Haskell", user-defined types are explained and then the notion that built-in types are, apart from a special syntax, no more different than user-defined ones:
Earlier we introduced several "built-in" types such as lists, tuples, integers, and characters. We have also shown how new user-defined types can be defined. Aside from special syntax, are the built-in types in any way more special than the user-defined ones? The answer is no. (The special syntax is for convenience and for consistency with historical convention, but has no semantic consequences.)
So you could define a tuple like to the following:
data (a,b) = (a,b)
data (a,b,c) = (a,b,c)
data (a,b,c,d) = (a,b,c,d)
Which sure you cannot because that would require an infinite number of declarations. So how are these types actually implemented? Especially regarding the fact that only against a type declaration you can pattern-match?
Since GHC is open source, we can just look at it:
The tuples are a lot less magical than you think:
From https://github.com/ghc/ghc/blob/master/libraries/ghc-prim/GHC/Tuple.hs
data (a,b) = (a,b)
data (a,b,c) = (a,b,c)
data (a,b,c,d) = (a,b,c,d)
data (a,b,c,d,e) = (a,b,c,d,e)
data (a,b,c,d,e,f) = (a,b,c,d,e,f)
data (a,b,c,d,e,f,g) = (a,b,c,d,e,f,g)
-- and so on...
So, tuples with different arities are just different data types, and tuples with very big number of arities is not supported.
Lists are also around there:
From https://github.com/ghc/ghc/blob/master/libraries/ghc-prim/GHC/Types.hs#L101
data [] a = [] | a : [a]
But there is a little bit of magic (special syntax) for lists.
Note: I know that GitHub is not where GHC is developed, but searching "ghc source code" on Google did not yield the correct page, and GitHub was the easiest.