[Haskell-cafe] Tupling functions

Victor Nazarov asviraspossible at gmail.com
Wed Sep 14 15:32:33 CEST 2011

On Wed, Sep 14, 2011 at 6:45 AM, Casey McCann <cam at uptoisomorphism.net> wrote:
> On Tue, Sep 13, 2011 at 10:03 PM, Chris Smith <cdsmith at gmail.com> wrote:
>> Ah, okay... then sure, you can do this:
>> class Tuple a b c | a b -> c where
>>    tuple :: a -> b -> c
>> instance Tuple (a -> b, a -> c) a (b,c) where
>>    tuple (f,g) x = (f x, g x)
> This wouldn't actually work well in practice. There's no dependency
> between the various occurrences of "a" in the types, so unless they're
> already known to be the same, GHC will complain about an ambiguous
> instance (please excuse the silly GHCi prompt):
>    Ok, modules loaded: Tupling.
>    ∀x. x ⊢ tuple ((+3), show) 4
>    <interactive>:0:1:
>        No instance for (Tuple (a0 -> a0, a1 -> String) b0 c0)
>          arising from a use of `tuple'
> Given that the class is only intended to be used where those types are
> equal, you really want it to unify them based on use of the tuple
> function.
>> and so on...  You'll need fundeps (or type families if you prefer to
>> write it that way), and probably at least flexible and/or overlapping
>> instances, too, but of course GHC will tell you about those.
> I rather prefer type families in this case, both because the problem
> is easily expressed in "type function" style, and because it gives you
> an easy type equality constraint to use, rather than using arcane
> trickery with overlaps to force post-hoc unification. We'd probably
> want to do something like this:
>    class Tuple t where
>        type Arg t :: *
>        type Result t :: *
>        tuple :: t -> Arg t -> Result t
>    instance (x1 ~ x2) => Tuple (x1 -> a, x2 -> b) where
>        type Arg (x1 -> a, x2 -> b) = x1
>        type Result (x1 -> a, x2 -> b) = (a, b)
>        tuple (f, g) x = (f x, g x)
>    instance (x1 ~ x2, x2 ~ x3) => Tuple (x1 -> a, x2 -> b, x3 -> c) where
>        type Arg (x1 -> a, x2 -> b, x3 -> c) = x1
>        type Result (x1 -> a, x2 -> b, x3 -> c) = (a, b, c)
>        tuple (f, g, h) x = (f x, g x, h x)
> Used like so:
>    Ok, modules loaded: Tupling.
>    ∀x. x ⊢ tuple ((+2), show, (< 2)) 3
>    (5,"3",False)
> Note that not only does this avoid ambiguity, it even unifies
> ambiguous types that are then defaulted by the usual means.
> That said, I question the utility of a class like this. The
> boilerplate instances are tedious to write and it's not flexible in
> any way; tuples not being defined inductively makes them a real pain
> to work with unless there's a particularly good reason to do so.
> Something equivalent to right-nested (,) with () as a terminator is
> much more pleasant, and since we're deep in the pits of
> non-portability anyway, might as well pull out bang patterns and
> UNPACK pragmas if avoiding extra bottoms was the reason for using
> plain tuples.

I've just tried another approach (code below). And GHC even inferred
type for tupleF. But I think GHC inferred the wrong type and I can't
formulate the right one, it seems to require infinite number of
constraints. With GHC inferred type this function is not usable,

*Tuples> tupleF ((+2), show, (<2)) 3

    Couldn't match expected type `TTail
                                    (a -> a, a1 -> String, a2 -> Bool)'
           against inferred type `(a -> a, a1 -> String, a2 -> Bool)'
      NB: `TTail' is a type function, and may not be injective
    When generalising the type(s) for `it'

{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE FlexibleContexts #-}
module Tuples where

class Tuple t
class (Tuple t, Tuple (TTail t)) => NTuple t
    type THead t :: *
    type TTail t :: *
    thead :: t -> THead t
    ttail :: t -> TTail t
    tcons :: THead t -> TTail t -> t

newtype Tuple1 a = Tuple1 a

instance Tuple ()
instance Tuple (Tuple1 a)
instance NTuple (Tuple1 a)
    type THead (Tuple1 a) = a
    type TTail (Tuple1 a) = ()
    thead (Tuple1 a) = a
    ttail (Tuple1 a) = ()
    tcons a b = Tuple1 a
instance Tuple (a, b)
instance NTuple (a, b)
    type THead (a, b) = a
    type TTail (a, b) = Tuple1 b
    thead (a, b) = a
    ttail (a, b) = Tuple1 b
    tcons a (Tuple1 b) = (a, b)
instance Tuple (a, b, c)
instance NTuple (a, b, c)
    type THead (a, b, c) = a
    type TTail (a, b, c) = (b, c)
    thead (a, b, c) = a
    ttail (a, b, c) = (b, c)
    tcons a (b, c) = (a, b, c)

tupleF t a = thead t a `tcons` tupleF (ttail t) a

Victor Nazarov

More information about the Haskell-Cafe mailing list