# Tuple-like constructors

Robert Dockins robdockins at fastmail.fm
Mon Feb 6 15:43:59 EST 2006

```On Feb 6, 2006, at 2:40 PM, Mieszko Lis wrote:

> i would argue against treating tuples as pure syntactic sugar for
> nested pairs; since the nesting carries hierarchical information, i
> would expect (x,y,z) used in place of (x,(y,z)) to cause an error.

Well, these would be desugared differently....

(x,y,z) === Tuple x (Tuple y (Tuple z ()))
(x,(y,z)) === Tuple x (Tuple (Tuple y (Tuple z ())) ())

which maintains the distinction, just like in lisp (maybe I remember
lisp syntax...) where

'(x y z)

is not the same as

'(x '(y z))

> bluespec classic implemented n-tuples this way, and the error
> messages were rather ugly.  not to mention that explaining this to
> beginners feels like a "oh yeah, it's not clean, it was an easy
> hack" copout :)

I'm pretty sure GHC, at least, already does type synonym folding for
error messages.  I suspect the same or a similar mechanism could be
used to resugar tuples for messages.

I also don't think it's a copout.  Set theory formulations of
classical mathematics usually defines n-tuples using nested 2-
tuples.  At least one constructive formulation of mathematics I know
of defines n-tuples as nested pairs (the Coq theorem prover, which
also provides syntactic sugar).  It's a nice clean inductive
definition and effortlessly allows tuples of arbitrary length.

The copout (in my opinion) is the current system where tuples are
defined up to some set n (64 in GHC I think -- the report only
mandates 15), and if you want bigger tuples you're SOL.  Why would
anyone ever use a 15+ tuple?  Well maybe you wouldn't, but perhaps a
code generator would.  Or maybe a joy interpreter would.  etc.

More disturbing is the complete inability to write general functions
over tuples.  You can't even play fancy typeclass tricks because
tuple constructors are not related by the type system in any way --
you have to do it the hard way each and every time by writing out
various versions at different arities until you get tired of typing.
Or, if you are a little more industrious, you can write a code
generator.  Either way, your function can only be defined up to some
fixed limit.  The only fully general solution is to use something
like template haskell which 1) is overkill 2) has ugly syntax (sorry
but it's true) 3) GHC only and 4) hard to learn.

Rob Dockins

> -- m
>
> On Feb 4, 2006, at 11:03 PM, Robert Dockins wrote:
>>
>> On Feb 4, 2006, at 7:56 PM, Pablo Barenbaum wrote:
>>
>>> An awkwardness in Haskell I would like to see solved in
>>> Haskell', is the fact that the behavior of tuple-like
>>> constructors must be either built-in or "limited".
>>
>> One thing I recall seeing on haskell-cafe some time back was the
>> notion that an n-tuple is semantically equivalent to n nested
>> right-strict 2-tuples (essentially right-strict heterogeneous
>> lists).  Perhaps we should consider the idea of the tuple notation
>> simply being syntactic sugar for nested right-strict 2-tuples.
>> Consider:
>>
>> data Tuple a b = Tuple a !b
>>
>> -- (a,b)    === Tuple a (Tuple b ())-- (a,b,c) === Tuple a (Tuple
>> b (Tuple c ()))
>> -- etc...
>>
>> fst (Tuple x _) = x
>> snd (Tuple x (Tuple y _)) = y
>>
>> fst ('a',b') = 'a'
>> snd (a','b) = 'b'
>>
>> fst ('a','b','c') = 'a'
>> snd ('a','b','c') = 'b'
>>
>> fst ('a','b','c','d','e','f') = 'a'
>> -- etc...
>>
>> It seems like compiler cleverness could recover the identical
>> strictness and unboxing information available now when the "shape"
>> of the tuple is known at compile time.
>>
>>
>>
>>> As far as I can see, these are two issues:
>>>
>>> 1. There is not a way, for the programmer, to define
>>> infinite constructors for infinite associated types (such as
>>> (,) (,,) (,,,) ... / (a, b) (a, b, c) (a, b, c, d) ...)
>>> these must be built-in.
>>>
>>> 2. There is not a way to define functions operating on all
>>> of these types. Instead, different functions (like zip,
>>> zip3) must be defined. Something similar happens with
>>> liftM, liftM2, ... these are "limited".
>>>
>>> It seems the language is lacking abstraction, or being
>>> misused, when the standard prelude issues this code:
>>>
>>>   zip              :: [a] -> [b] -> [(a,b)]
>>>   zip               = zipWith  (\a b -> (a,b))
>>>
>>>   zip3             :: [a] -> [b] -> [c] -> [(a,b,c)]
>>>   zip3              = zipWith3 (\a b c -> (a,b,c))
>>>
>>> Clearly, at least for a human being, it's redundant.
>>> I don't know if there already are proposals to solve this.
>>>
>>> Sorry if I sound aggresive, I'm just trying to help.
>>> Excuse my English...
>>>
>>> Best regards.

```