Tuples

Posted on 2014-04-02 by nbloomf
Tags: arithmetic-made-difficult, literate-haskell

This page is part of a series on Arithmetic Made Difficult.

This post is literate Haskell; you can load the source into GHCi and play along.


{-# LANGUAGE NoImplicitPrelude, FlexibleContexts #-}
module Tuples
  ( Pair(Pair,fst,snd), dup, tup, tswap, tpair, tassocL, tassocR
  , uncurry, curry, _test_tuple, main_tuple
  ) where

import Testing
import Functions
import Flip
import Clone
import Composition
import Booleans
import And

Today we’ll establish a few basic utility functions on tuples. First, recall some definitions.

Let \(A\) and \(B\) be sets. There is a set \(A \times B\) together with two functions \(\fst : A \times B \rightarrow A\) and \(\snd : A \times B \rightarrow B\) having the property that if \(X\) is a set and \(\sigma : X \rightarrow A\) and \(\tau : X \rightarrow B\) functions then there is a unique map \(\Theta : X \rightarrow A \times B\) such that \(\fst \circ \Theta = \sigma\) and \(\snd \circ \Theta = \tau\). That is, there is a unique \(\Theta\) such that the following diagram commutes.

\[\require{AMScd} \begin{CD} X @= X @= X \\ @V{\sigma}VV @VV{\Theta}V @VV{\tau}V \\ A @<<{\fst}< A \times B @>>{\snd}> B \end{CD}\]

We will denote this unique \(\Theta : X \rightarrow A \times B\) by \(\dup(\sigma,\tau)\).

More concretely, if \(f : X \rightarrow A \times B\) such that \(\fst(f(x)) = \sigma(x)\) and \(\snd(f(x)) = \tau(x)\) for all \(x \in X\), then \(f = \dup(\sigma,\tau)\).

Now \(A \times B\) uniquely represents all possible pairs of elements of \(A\) and \(B\) in a precise sense.

Let \(A\) and \(B\) be sets. Given \(a \in A\) and \(b \in B\), we define \(\tup : A \rightarrow B \rightarrow A \times B\) by \[\tup(a)(b) = \dup(\const(a),\const(b))(\ast).\] Now we have the following.

  1. \(\fst(\tup(a)(b)) = a\) and \(\snd(\tup(a)(b)) = b\).
  2. If \(w \in A \times B\) such that \(\fst(w) = a\) and \(\snd(w) = b\), then \(w = \tup(a)(b)\).
  3. \(\beq(\tup(a)(b),\tup(c)(d)) = \band(\beq(a,c),\beq(b,d))\).
  1. We have \[\begin{eqnarray*} & & \fst(\tup(a)(b)) \\ & \href{/posts/arithmetic-made-difficult/Tuples.html#def-tup} = & \fst(\dup(\const(a),\const(b))(\ast)) \\ & \href{/posts/arithmetic-made-difficult/Tuples.html#def-fst-dup} = & \const(a)(\ast) \\ & \href{/posts/arithmetic-made-difficult/Functions.html#def-const} = & a \end{eqnarray*}\] and \[\begin{eqnarray*} & & \snd(\tup(a)(b)) \\ & \href{/posts/arithmetic-made-difficult/Tuples.html#def-tup} = & \snd(\dup(\const(a),\const(b))(\ast)) \\ & \href{/posts/arithmetic-made-difficult/Tuples.html#def-snd-dup} = & \const(b)(\ast) \\ & \href{/posts/arithmetic-made-difficult/Functions.html#def-const} = & b \end{eqnarray*}\] as claimed.
  2. Suppose \(\fst(w) = a\) and \(\snd(w) = b\), and consider \(\const(w)\) as a function \(\ast \rightarrow A \times B\). Note that \[\begin{eqnarray*} & & \fst(\const(w)(\ast)) \\ & \href{/posts/arithmetic-made-difficult/Functions.html#def-const} = & \fst(w) \\ & \hyp{\fst(w) = a} = & a \\ & \href{/posts/arithmetic-made-difficult/Functions.html#def-const} = & \const(a)(\ast) \end{eqnarray*}\] and \[\begin{eqnarray*} & & \snd(\const(w)(\ast)) \\ & \href{/posts/arithmetic-made-difficult/Functions.html#def-const} = & \snd(w) \\ & \hyp{\snd(w) = b} = & b \\ & \href{/posts/arithmetic-made-difficult/Functions.html#def-const} = & \const(b)(\ast) \end{eqnarray*}\] so by the universal property of \(A \times B\), \(w = \tup(a)(b)\).
  3. First suppose that \[\beq(\tup(a)(b),\tup(c)(d));\] then \[a = \fst(\tup(a)(b)) = \fst(\tup(c)(d)) = c\] and \[b = \snd(\tup(a)(b)) = \snd(\tup(c)(d)) = d\] so that \[\band(\beq(a,c),\beq(b,d)).\] Conversely, suppose \(\band(\beq(a,c),\beq(b,d))\). Then we have \[\fst(\tup(a)(b)) = a = c = \fst(\tup(c)(d))\] and \[\snd(\tup(a)(b)) = b = d = \snd(\tup(c)(d)).\] By (2), we have \(\tup(a)(b) = \tup(c)(d)\).

In Haskell we can model \(A \times B\), and the maps in the universal property, with the following type.

data Pair a b = Pair
  { fst :: a
  , snd :: b
  } deriving Show

dup :: (x -> a) -> (x -> b) -> x -> Pair a b
dup f g x = Pair (f x) (g x)

tup :: a -> b -> Pair a b
tup a b = dup (const a) (const b) undefined

instance (Equal a, Equal b) => Equal (Pair a b) where
  eq a b = and (eq (fst a) (fst b)) (eq (snd a) (snd b))

instance (Arbitrary a, Arbitrary b) => Arbitrary (Pair a b) where
  arbitrary = do
    a <- arbitrary
    b <- arbitrary
    return (Pair a b)

instance (CoArbitrary a, CoArbitrary b) => CoArbitrary (Pair a b) where
  coarbitrary (Pair a b) = coarbitrary a . coarbitrary b

instance (TypeName a, TypeName b) => TypeName (Pair a b) where
  typeName (Pair a b) = "Pair " ++ typeName a ++ " " ++ typeName b

\(\dup\) is a \(\tup\).

Let \(A\) and \(B\) be sets. For all \(x \in A \times B\) we have \[\dup(f,g)(x) = \tup(f(x))(g(x)).\]

We have \[\begin{eqnarray*} & & \fst(\dup(f,g)(x)) \\ & \href{/posts/arithmetic-made-difficult/Tuples.html#def-fst-dup} = & f(x) \end{eqnarray*}\] and \[\begin{eqnarray*} & & \snd(\dup(f,g)(x)) \\ & \href{/posts/arithmetic-made-difficult/Tuples.html#def-snd-dup} = & g(x) \end{eqnarray*}\] so that \(\dup(f,g)(x) = \tup(f(x))(g(x))\) by the uniqueness of \(\tup\). as claimed.

For example, \(\id_{A \times B}\) is a \(\dup\).

Provided the types match up, we have \[\dup(\fst,\snd) = \id_{A \times B}.\]

Note that \[\begin{eqnarray*} & & \fst(\dup(\fst,\snd)(\tup(a)(b))) \\ & \href{/posts/arithmetic-made-difficult/Tuples.html#def-fst-dup} = & \fst(\tup(a)(b)) \\ & \href{/posts/arithmetic-made-difficult/Tuples.html#thm-fst-tup} = & a \end{eqnarray*}\] and \[\begin{eqnarray*} & & \snd(\dup(\fst,\snd)(\tup(a)(b))) \\ & \href{/posts/arithmetic-made-difficult/Tuples.html#def-snd-dup} = & \snd(\tup(a)(b)) \\ & \href{/posts/arithmetic-made-difficult/Tuples.html#thm-snd-tup} = & b \end{eqnarray*}\] so that \(\dup(\fst,\snd)(\tup(a)(b)) = \tup(a)(b)\) for all \(a \in A\) and \(b \in B\).

We define \(\tSwap\) on tuples like so.

Let \(A\) and \(B\) be sets. We define \(\tSwap : A \times B \rightarrow B \times A\) by \[\tSwap = \dup(\snd,\fst).\]

In Haskell,

Elements of \(A \times B\) act like “ordered pairs”, and \(\tSwap\) effectively reverses the order of the pair.

Let \(A\) and \(B\) be sets. Then we have the following.

  1. \(\tSwap(\tup(a)(b)) = \tup(b)(a)\).
  2. \(\tSwap(\tSwap(\tup(a)(b))) = \tup(a)(b)\).
  1. Note that \[\begin{eqnarray*} & & \fst(\tSwap(\tup(a)(b))) \\ & \href{/posts/arithmetic-made-difficult/Tuples.html#def-tSwap} = & \fst(\dup(\snd,\fst)(\tup(a)(b))) \\ & \href{/posts/arithmetic-made-difficult/Tuples.html#def-fst-dup} = & \snd(\tup(a)(b)) \\ & \href{/posts/arithmetic-made-difficult/Tuples.html#thm-snd-tup} = & b \end{eqnarray*}\] and \[\begin{eqnarray*} & & \snd(\tSwap(\tup(a)(b))) \\ & \href{/posts/arithmetic-made-difficult/Tuples.html#def-tSwap} = & \snd(\dup(\snd,\fst)(\tup(a)(b))) \\ & \href{/posts/arithmetic-made-difficult/Tuples.html#def-snd-dup} = & \fst(\tup(a)(b)) \\ & \href{/posts/arithmetic-made-difficult/Tuples.html#thm-fst-tup} = & a \end{eqnarray*}\] so that \(\tSwap(\tup(a)(b)) = \tup(b)(a)\) as claimed.
  2. Note that \[\begin{eqnarray*} & & \tSwap(\tSwap(\tup(a)(b))) \\ & \href{/posts/arithmetic-made-difficult/Tuples.html#thm-tSwap-swap} = & \tSwap(\tup(b)(a)) \\ & \href{/posts/arithmetic-made-difficult/Tuples.html#thm-tSwap-swap} = & \tup(a)(b) \end{eqnarray*}\] as claimed.

Next, the utility \(\tPair\) facilitates defining functions from one tuple to another.

Let \(A\), \(B\), \(U\), and \(V\) be sets. We define \(\tPair : U^A \times V^B \rightarrow (U \times V)^{A \times B}\) by \[\tPair(f,g) = \dup(\compose(f)(\fst),\compose(g)(\snd)).\]

In Haskell:

\(\tPair\) has some nice properties.

For all \(f\), \(g\), \(h\), \(k\), \(a\), and \(b\) we have the following.

  1. \(\tPair(f,g)(\tup(a)(b)) = \tup(f(a))(g(b))\).
  2. \(\compose(\tPair(f,g))(\tPair(h,k)) = \tPair(\compose(f)(h),\compose(g)(k))\).
  1. Note that \[\begin{eqnarray*} & & \tPair(f,g)(\tup(a)(b)) \\ & \href{/posts/arithmetic-made-difficult/Tuples.html#def-tPair} = & \dup(\compose(f)(\fst))(\compose(g)(\snd))(\tup(a)(b)) \\ & \href{/posts/arithmetic-made-difficult/Tuples.html#thm-dup-tup} = & \tup(\compose(f)(\fst)(\tup(a)(b)))(\compose(g)(\snd)(\tup(a)(b))) \\ & \href{/posts/arithmetic-made-difficult/Functions.html#def-compose} = & \tup(f(\fst(\tup(a)(b))))(\compose(g)(\snd)(\tup(a)(b))) \\ & \href{/posts/arithmetic-made-difficult/Functions.html#def-compose} = & \tup(f(\fst(\tup(a)(b))))(g(\snd(\tup(a)(b)))) \\ & \href{/posts/arithmetic-made-difficult/Tuples.html#thm-fst-tup} = & \tup(f(a))(g(\snd(\tup(a)(b)))) \\ & \href{/posts/arithmetic-made-difficult/Tuples.html#thm-snd-tup} = & \tup(f(a))(g(b)) \end{eqnarray*}\] as claimed.
  2. Note that \[\begin{eqnarray*} & & \compose(\tPair(f,g))(\tPair(h,k))(\tup(a)(b)) \\ & \href{/posts/arithmetic-made-difficult/Functions.html#def-compose} = & \tPair(f,g)(\tPair(h,k)(\tup(a)(b))) \\ & \href{/posts/arithmetic-made-difficult/Tuples.html#thm-tPair-tup} = & \tPair(f,g)(\tup(h(a))(k(b))) \\ & \href{/posts/arithmetic-made-difficult/Tuples.html#thm-tPair-tup} = & \tup(f(h(a)))(g(k(b))) \\ & \href{/posts/arithmetic-made-difficult/Functions.html#def-compose} = & \tup(f(h(a)))(\compose(g)(k)(b)) \\ & \href{/posts/arithmetic-made-difficult/Functions.html#def-compose} = & \tup(\compose(f)(h)(a))(\compose(g)(k)(b)) \\ & \href{/posts/arithmetic-made-difficult/Tuples.html#thm-tPair-tup} = & \tPair(\compose(f)(h),\compose(g)(k))(\tup(a)(b)) \end{eqnarray*}\] as claimed.

Finally, note that although as sets \(A \times (B \times C)\) and \((A \times B) \times C\) cannot possibly be equal to each other in general, they are naturally isomorphic via \(\tAssocL\) and \(\tAssocR\).

Let \(A\), \(B\), and \(C\) be sets. We define \(\tAssocL : A \times (B \times C) \rightarrow (A \times B) \times C\) by \[\tAssocL = \dup(\dup(\fst,\compose(\fst)(\snd)),\compose(\snd)(\snd))\] and define \(\tAssocR : (A \times B) \times C \rightarrow A \times (B \times C)\) by \[\tAssocR = \dup(\compose(\fst)(\fst),\dup(\compose(\snd)(\fst),\snd)).\]

In Haskell:

Now \(\tAssocL\) and \(\tAssocR\) have some nice properties.

The following hold whenever everything has the appropriate type.

  1. \(\tAssocL(\tup(a)(\tup(b)(c))) = \tup(\tup(a)(b))(c)\).
  2. \(\tAssocR(\tup(\tup(a)(b))(c)) = \tup(a)(\tup(b)(c))\).
  3. \(\compose(\tAssocR)(\tAssocL) = \id\).
  4. \(\compose(\tAssocL)(\tAssocR) = \id\).
  1. Note that \[\begin{eqnarray*} & & \tAssocL(\tup(a)(\tup(b)(c))) \\ & \href{/posts/arithmetic-made-difficult/Tuples.html#def-tAssocL} = & \dup(\dup(\fst,\compose(\fst)(\snd)),\compose(\snd)(\snd))(\tup(a)(\tup(b)(c))) \\ & \href{/posts/arithmetic-made-difficult/Tuples.html#thm-dup-tup} = & \tup(\dup(\fst,\compose(\fst)(\snd))(\tup(a)(\tup(b)(c))))(\compose(\snd)(\snd)(\tup(a)(\tup(b)(c)))) \\ & \href{/posts/arithmetic-made-difficult/Functions.html#def-compose} = & \tup(\dup(\fst,\compose(\fst)(\snd))(\tup(a)(\tup(b)(c))))(\snd(\snd(\tup(a)(\tup(b)(c))))) \\ & \href{/posts/arithmetic-made-difficult/Tuples.html#thm-snd-tup} = & \tup(\dup(\fst,\compose(\fst)(\snd))(\tup(a)(\tup(b)(c))))(\snd(\tup(b)(c))) \\ & \href{/posts/arithmetic-made-difficult/Tuples.html#thm-snd-tup} = & \tup(\dup(\fst,\compose(\fst)(\snd))(\tup(a)(\tup(b)(c))))(c) \\ & \href{/posts/arithmetic-made-difficult/Tuples.html#thm-dup-tup} = & \tup(\tup(\fst(\tup(a)(\tup(b)(c))))(\compose(\fst)(\snd)(\tup(a)(\tup(b)(c)))))(c) \\ & \href{/posts/arithmetic-made-difficult/Functions.html#def-compose} = & \tup(\tup(\fst(\tup(a)(\tup(b)(c))))(\fst(\snd(\tup(a)(\tup(b)(c))))))(c) \\ & \href{/posts/arithmetic-made-difficult/Tuples.html#thm-snd-tup} = & \tup(\tup(\fst(\tup(a)(\tup(b)(c))))(\fst(\tup(b)(c))))(c) \\ & \href{/posts/arithmetic-made-difficult/Tuples.html#thm-fst-tup} = & \tup(\tup(\fst(\tup(a)(\tup(b)(c))))(b))(c) \\ & \href{/posts/arithmetic-made-difficult/Tuples.html#thm-fst-tup} = & \tup(\tup(a)(b))(c) \end{eqnarray*}\] as claimed.
  2. Note that \[\begin{eqnarray*} & & \tAssocR(\tup(\tup(a)(b))(c)) \\ & \href{/posts/arithmetic-made-difficult/Tuples.html#def-tAssocR} = & \dup(\compose(\fst)(\fst),\dup(\compose(\snd)(\fst),\snd))(\tup(\tup(a)(b))(c)) \\ & \href{/posts/arithmetic-made-difficult/Tuples.html#thm-dup-tup} = & \tup(\compose(\fst)(\fst)(\tup(\tup(a)(b))(c)))(\dup(\compose(\snd)(\fst),\snd)(\tup(\tup(a)(b))(c))) \\ & \href{/posts/arithmetic-made-difficult/Functions.html#def-compose} = & \tup(\fst(\fst(\tup(\tup(a)(b))(c))))(\dup(\compose(\snd)(\fst),\snd)(\tup(\tup(a)(b))(c))) \\ & \href{/posts/arithmetic-made-difficult/Tuples.html#thm-fst-tup} = & \tup(\fst(\tup(a)(b)))(\dup(\compose(\snd)(\fst),\snd)(\tup(\tup(a)(b))(c))) \\ & \href{/posts/arithmetic-made-difficult/Tuples.html#thm-fst-tup} = & \tup(a)(\dup(\compose(\snd)(\fst),\snd)(\tup(\tup(a)(b))(c))) \\ & \href{/posts/arithmetic-made-difficult/Tuples.html#thm-dup-tup} = & \tup(a)(\tup(\compose(\snd)(\fst)(\tup(\tup(a)(b))(c)))(\snd(\tup(\tup(a)(b))(c)))) \\ & \href{/posts/arithmetic-made-difficult/Tuples.html#thm-snd-tup} = & \tup(a)(\tup(\compose(\snd)(\fst)(\tup(\tup(a)(b))(c)))(c)) \\ & \href{/posts/arithmetic-made-difficult/Functions.html#def-compose} = & \tup(a)(\tup(\snd(\fst(\tup(\tup(a)(b))(c))))(c)) \\ & \href{/posts/arithmetic-made-difficult/Tuples.html#thm-fst-tup} = & \tup(a)(\tup(\snd(\tup(a)(b)))(c)) \\ & \href{/posts/arithmetic-made-difficult/Tuples.html#thm-snd-tup} = & \tup(a)(\tup(b)(c)) \end{eqnarray*}\] as claimed.
  3. Note that \[\begin{eqnarray*} & & \compose(\tAssocR)(\tAssocL)(\tup(a)(\tup(b)(c))) \\ & \href{/posts/arithmetic-made-difficult/Functions.html#def-compose} = & \tAssocR(\tAssocL(\tup(a)(\tup(b)(c)))) \\ & \href{/posts/arithmetic-made-difficult/Tuples.html#thm-tAssocL-tup} = & \tAssocR(\tup(\tup(a)(b))(c)) \\ & \href{/posts/arithmetic-made-difficult/Tuples.html#thm-tAssocR-tup} = & \tup(a)(\tup(b)(c)) \end{eqnarray*}\] as claimed.
  4. Note that \[\begin{eqnarray*} & & \compose(\tAssocL)(\tAssocR)(\tup(\tup(a)(b))(c)) \\ & \href{/posts/arithmetic-made-difficult/Functions.html#def-compose} = & \tAssocL(\tAssocR(\tup(\tup(a)(b))(c))) \\ & \href{/posts/arithmetic-made-difficult/Tuples.html#thm-tAssocR-tup} = & \tAssocL(\tup(a)(\tup(b)(c))) \\ & \href{/posts/arithmetic-made-difficult/Tuples.html#thm-tAssocL-tup} = & \tup(\tup(a)(b))(c) \end{eqnarray*}\] as claimed.

Higher-order functions can be converted to functions on tuples (and back).

Let \(A\), \(B\), and \(C\) be sets. If \(f : A \rightarrow B \rightarrow C\), we define \(\uncurry(f) : A \times B \rightarrow C\) by \[\uncurry(f)(\tup(a)(b)) = f(a)(b).\] If \(g : A \times B \rightarrow C\), we define \(\curry(g) : A \rightarrow B \rightarrow C\) by \[\curry(g)(a)(b) = g(\tup(a)(b)).\]

In Haskell:

Testing

Suite:

_test_tuple
  :: ( Equal a, Show a, Arbitrary a, CoArbitrary a, TypeName a
     , Equal b, Show b, Arbitrary b, CoArbitrary b, TypeName b
     , Equal c, Show c, Arbitrary c, CoArbitrary c, TypeName c
  ) => Int -> Int -> a -> b -> c -> IO ()
_test_tuple size cases a b c = do
  testLabel3 "Tuple" a b c
  let args = testArgs size cases

  runTest args (_test_dup_fst_snd a b)
  runTest args (_test_dup_tup a b)
  runTest args (_test_tswap_entries a b)
  runTest args (_test_tswap_tswap a b)
  runTest args (_test_tpair_apply a b)
  runTest args (_test_tpair_tpair a b)
  runTest args (_test_tassocL_entries a b c)
  runTest args (_test_tassocR_entries a b c)
  runTest args (_test_tassocL_tassocR a b c)
  runTest args (_test_tassocR_tassocL a b c)

Main:

main_tuple :: IO ()
main_tuple = do
  _test_tuple 20 100 (true :: Bool) (true :: Bool) (true :: Bool)

  let a = tup true true :: Pair Bool Bool

  _test_functions 20 100 a a a a
  _test_flip      20 100 a a a a a a a
  _test_clone     20 100 a a
  _test_compose   20 100 a a a a a a a