Cat

Posted on 2017-04-25 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 #-}
module Cat
  ( cat, _test_cat, main_cat
  ) where

import Testing
import Booleans
import And
import NaturalNumbers
import Lists
import HeadAndTail
import Snoc
import Reverse

In this post we’ll consider the function that takes two lists and appends one to the “end” of the other. This function is known as \(\cat\), which is short for catenate – a jargony word that means to connect in a series.

We define a map \(\cat : \lists{A} \times \lists{A} \rightarrow \lists{A}\) by \[\cat(x,y) = \foldr(y)(\cons)(x).\]

In Haskell:

Because \(\cat\) is defined in terms of fold, it is the unique solution to a system of functional equations.

Let \(A\) be a set. Then \(\cat\) is the unique mapping \(f : \lists{A} \times \lists{A} \rightarrow \lists{A}\) with the property that for all \(a \in A\) and \(x,y \in \lists{A}\) we have \[\left\{\begin{array}{l} f(\nil,y) = y \\ f(\cons(a,x),y) = \cons(a,f(x,y)). \end{array}\right.\]

Note that \(\cat\) works a lot like \(\snoc\); it marches down the list \(x\) until it reaches the end, and then sticks \(y\) there. In some ways, \(\cat\) is like \(\nplus\) for lists, and \(\nil\) is the \(\zero\).

Let \(A\) be a set. For all \(x \in \lists{A}\), we have \(\cat(x,\nil) = x\).

Note that \[\begin{eqnarray*} & & \cat(x,\nil) \\ & \href{/posts/arithmetic-made-difficult/Cat.html#def-cat} = & \foldr(\nil)(\cons)(x) \\ & \href{/posts/arithmetic-made-difficult/Lists.html#thm-foldr-nil-cons} = & \id(x) \\ & \href{/posts/arithmetic-made-difficult/Functions.html#def-id} = & x \end{eqnarray*}\] as claimed.

\(\cat\) interacts with \(\snoc\):

Let \(A\) be a set. The following hold for all \(a \in A\) and \(x,y \in \lists{A}\).

  1. \(\cat(\snoc(a,x),y) = \cat(x,\cons(a,y))\).
  2. \(\cat(x,\snoc(a,y)) = \snoc(a,\cat(x,y))\).
  1. We have \[\begin{eqnarray*} & & \cat(x,\cons(a,y)) \\ & \href{/posts/arithmetic-made-difficult/Cat.html#def-cat} = & \foldr(\cons(a,y))(\cons)(x) \\ & = & \foldr(y)(\cons)(\snoc(a,y)) \\ & = & \cat(x,\snoc(a,y)) \end{eqnarray*}\] as claimed.
  2. We proceed by list induction on \(y\). For the base case \(y = \nil\) we have \[\begin{eqnarray*} & & \snoc(a,\cat(x,\nil)) \\ & \href{/posts/arithmetic-made-difficult/Cat.html#thm-cat-nil-right} = & \snoc(a,x) \\ & \href{/posts/arithmetic-made-difficult/Snoc.html#def-snoc} = & \foldr(\cons(a,\nil))(\cons)(x) \\ & = & \cat(x,\cons(a,\nil)) \end{eqnarray*}\] as claimed. For the inductive step, suppose the equality holds for some \(y \in \lists{A}\), and let \(b \in A\). Now \[\begin{eqnarray*} & & \snoc(a,\cat(x,\cons(b,y))) \\ & \href{/posts/arithmetic-made-difficult/Cat.html#thm-cat-snoc-left} = & \snoc(a,\cat(\snoc(b,x),y)) \\ & \href{/posts/arithmetic-made-difficult/Cat.html#thm-cat-snoc-right} = & \cat(\snoc(b,x),\snoc(a,y)) \\ & \href{/posts/arithmetic-made-difficult/Cat.html#thm-cat-snoc-left} = & \cat(x,\cons(b,\snoc(a,y))) \\ & \href{/posts/arithmetic-made-difficult/Snoc.html#cor-snoc-cons} = & \cat(x,\snoc(a,\cons(b,y))) \end{eqnarray*}\] as needed.

We can “solve” a simple list equation.

We have \(\nil = \cat(x,y)\) if and only if \(x = y = \nil\).

The “only if” direction is clear. To see the “if” direction, suppose we have \(x,y \in \lists{A}\) such that \(\cat(x,y) = \nil\). If \(x = \cons(a,u)\), then \[\begin{eqnarray*} & & \btrue \\ & \href{/posts/arithmetic-made-difficult/HeadAndTail.html#thm-isnil-nil} = & \isnil(\nil) \\ & = & \isnil(\cat(x,y)) \\ & = & \isnil(\cat(\cons(a,u),y)) \\ & \href{/posts/arithmetic-made-difficult/Cat.html#cor-cat-cons} = & \isnil(\cons(a,\cat(u,y))) \\ & \href{/posts/arithmetic-made-difficult/HeadAndTail.html#thm-isnil-cons} = & \bfalse \end{eqnarray*}\] which is absurd. Thus \(x = \nil\), and we have \[\begin{eqnarray*} & & \btrue \\ & = & \isnil(\cat(\nil,y)) \\ & \href{/posts/arithmetic-made-difficult/Cat.html#cor-cat-nil} = & \isnil(y) \end{eqnarray*}\] as claimed.

And \(\cat\) is associative.

Let \(A\) be a set and \(x,y,z \in \lists{A}\). Then \[\cat(\cat(x,y),z) = \cat(x,\cat(y,z)).\]

We proceed by list induction on \(z\). For the base case \(z = \nil\), we have \[\begin{eqnarray*} & & \cat(\cat(x,y),\nil) \\ & \href{/posts/arithmetic-made-difficult/Cat.html#thm-cat-nil-right} = & \cat(x,y) \\ & \href{/posts/arithmetic-made-difficult/Cat.html#thm-cat-nil-right} = & \cat(x,\cat(y,\nil)) \end{eqnarray*}\] as needed. For the inductive step, suppose the equality holds for some \(z \in \lists{A}\), and let \(a \in A\). Then we have \[\begin{eqnarray*} & & \cat(\cat(x,y),\cons(a,z)) \\ & \href{/posts/arithmetic-made-difficult/Cat.html#thm-cat-snoc-left} = & \cat(\snoc(a,\cat(x,y)),z) \\ & \href{/posts/arithmetic-made-difficult/Cat.html#thm-cat-snoc-right} = & \cat(\cat(x,\snoc(a,y)),z) \\ & \href{/posts/arithmetic-made-difficult/Cat.html#thm-cat-associative} = & \cat(x,\cat(\snoc(a,y),z)) \\ & \href{/posts/arithmetic-made-difficult/Cat.html#thm-cat-snoc-left} = & \cat(x,\cat(y,\cons(a,z))) \end{eqnarray*}\] as needed.

And \(\rev\) is antidistributive over \(\cat\).

Let \(A\) be a set. For all \(x,y \in \lists{A}\) we have \[\rev(\cat(x,y)) = \cat(\rev(y),\rev(x)).\]

We proceed by list induction on \(y\). For the base case \(y = \nil\), we have \[\begin{eqnarray*} & & \rev(\cat(x,\nil)) \\ & \href{/posts/arithmetic-made-difficult/Cat.html#thm-cat-nil-right} = & \rev(x) \\ & \href{/posts/arithmetic-made-difficult/Cat.html#cor-cat-nil} = & \cat(\nil,\rev(x)) \\ & \href{/posts/arithmetic-made-difficult/Reverse.html#cor-rev-nil} = & \cat(\rev(\nil),\rev(x)) \end{eqnarray*}\] as needed. For the inductive step, suppose the equality holds for some \(y \in \lists{A}\) and let \(a \in A\). Then we have \[\begin{eqnarray*} & & \rev(\cat(x,\cons(a,y))) \\ & \href{/posts/arithmetic-made-difficult/Cat.html#thm-cat-snoc-left} = & \rev(\cat(\snoc(a,x),y)) \\ & \href{/posts/arithmetic-made-difficult/Cat.html#thm-rev-cat-antidistribute} = & \cat(\rev(y),\rev(\snoc(a,x))) \\ & = & \cat(\rev(y),\cons(a,\rev(x))) \\ & \href{/posts/arithmetic-made-difficult/Cat.html#thm-cat-snoc-left} = & \cat(\snoc(a,\rev(y)),\rev(x)) \\ & \href{/posts/arithmetic-made-difficult/Reverse.html#cor-rev-cons} = & \cat(\rev(\cons(a,y)),\rev(x)) \end{eqnarray*}\] as needed.

Finally, \(\cat\) is cancellative.

Let \(A\) be a set. For all \(x,y,z \in \lists{A}\) we have the following.

  1. \(\cat(z,x) = \cat(z,y)\) if and only if \(x = y\).
  2. \(\cat(x,z) = \cat(y,z)\) if and only if \(x = y\).
  1. The “only if” direction is clear. For the “if” direction we proceed by list induction on \(z\). For the base case \(z = \nil\), suppose \(\cat(z,x) = \cat(z,y)\). Then we have \[\begin{eqnarray*} & & x \\ & \href{/posts/arithmetic-made-difficult/Cat.html#cor-cat-nil} = & \cat(\nil,x) \\ & = & \cat(z,x) \\ & = & \cat(z,y) \\ & = & \cat(\nil,y) \\ & \href{/posts/arithmetic-made-difficult/Cat.html#cor-cat-nil} = & y \end{eqnarray*}\] as needed. For the inductive step, suppose the implication holds for some \(z\), and let \(a \in A\). Now suppose we have \[\cat(\cons(a,z),x) = \cat(\cons(a,z),y).\] Then we have \(\cons(a,\cat(z,x)) = \cons(a,\cat(z,y)),\)$ so that \[\cat(z,x) = \cat(z,y).\] By the inductive hypothesis, \(x = y\) as claimed.
  2. The “only if” direction is clear. For the “if” direction, note that \[\begin{eqnarray*} & & \cat(\rev(z),\rev(x)) \\ & \href{/posts/arithmetic-made-difficult/Cat.html#thm-rev-cat-antidistribute} = & \rev(\cat(x,z)) \\ & = & \rev(\cat(y,z)) \\ & \href{/posts/arithmetic-made-difficult/Cat.html#thm-rev-cat-antidistribute} = & \cat(\rev(z),\rev(y)) \end{eqnarray*}\] By (1), we have \(\rev(x) = \rev(y)\), and thus \(x = y\) as claimed.

One more.

Let \(A\) be a set. The following hold for all \(x,u,v \in \lists{A}\).

  1. If \(x = \cat(x,v)\) then \(v = \nil\).
  2. If \(x = \cat(u,x)\) then \(u = \nil\).
  3. If \(x = \cat(u,\cat(x,v))\) then \(u = v = \nil\).
  1. Note that \[\begin{eqnarray*} & & \cat(x,\nil) \\ & \href{/posts/arithmetic-made-difficult/Cat.html#thm-cat-nil-right} = & x \\ & = & \cat(x,v), \end{eqnarray*}\] so that \(v = \nil\) by cancellation.
  2. Note that \[\begin{eqnarray*} & & \cat(\nil,x) \\ & \href{/posts/arithmetic-made-difficult/Cat.html#cor-cat-nil} = & x \\ & = & \cat(u,x), \end{eqnarray*}\] so that \(u = \nil\) by cancellation.
  3. We proceed by list induction on \(x\). For the base case \(x = \nil\), if \(x = \cat(u,\cat(x,v))\) we have \[\begin{eqnarray*} & & \nil \\ & = & x \\ & = & \cat(u,\cat(x,v)) \\ & = & \cat(u,\cat(\nil,v)) \\ & \href{/posts/arithmetic-made-difficult/Cat.html#cor-cat-nil} = & \cat(u,v) \end{eqnarray*}\] so that \(v = u = \nil\) as needed. For the inductive step, suppose the implication holds for all \(u\) and \(v\) for some \(x\), and let \(a \in A\). Suppose further that \(\cons(a,x) = \cat(u,\cat(\cons(a,x),v))\) for some \(u\) and \(v\). We consider two possibilities for \(u\). If \(u = \cons(b,w)\), we have \[\begin{eqnarray*} & & \cons(a,x) \\ & = & \cat(u,\cat(\cons(a,x),v)) \\ & = & \cat(\cons(b,w),\cat(\cons(a,x),v)) \\ & \href{/posts/arithmetic-made-difficult/Cat.html#cor-cat-cons} = & \cons(b,\cat(w,\cat(\cons(a,x),v))) \end{eqnarray*}\] So in fact we have \(a = b\), and \[\begin{eqnarray*} & & x \\ & = & \cat(w,\cat(\cons(a,x),v)) \\ & \href{/posts/arithmetic-made-difficult/Cat.html#thm-cat-associative} = & \cat(\cat(w,\cons(a,x)),v) \\ & \href{/posts/arithmetic-made-difficult/Cat.html#thm-cat-snoc-left} = & \cat(\cat(\snoc(a,w),x),v) \\ & \href{/posts/arithmetic-made-difficult/Cat.html#thm-cat-associative} = & \cat(\snoc(a,w),\cat(x,v)) \end{eqnarray*}\] By the inductive hypothesis, we have \(\snoc(a,w) = \nil\), a contradiction. So we must have \(u = \nil\). Now \[\begin{eqnarray*} & & \cons(a,x) \\ & = & \cat(u,\cat(\cons(a,x),v)) \\ & = & \cat(\nil,\cat(\cons(a,x),v)) \\ & \href{/posts/arithmetic-made-difficult/Cat.html#cor-cat-nil} = & \cat(\cons(a,x),v) \end{eqnarray*}\] so that \(v = u = \nil\) as claimed.

Testing

Suite:

_test_cat ::
  ( TypeName a, Show a, Equal a, Arbitrary a
  , TypeName (t a), List t, Equal (t a), Show (t a), Arbitrary (t a)
  ) => Int -> Int -> t a -> IO ()
_test_cat size cases t = do
  testLabel1 "cat" t

  let args = testArgs size cases

  runTest args (_test_cat_nil_left t)
  runTest args (_test_cat_cons_left t)
  runTest args (_test_cat_nil_right t)
  runTest args (_test_cat_snoc_left t)
  runTest args (_test_cat_snoc_right t)
  runTest args (_test_cat_nil_nil t)
  runTest args (_test_cat_associative t)
  runTest args (_test_cat_rev t)
  runTest args (_test_cat_left_cancellative t)
  runTest args (_test_cat_right_cancellative t)
  runTest args (_test_cat_right_identity_unique t)
  runTest args (_test_cat_left_identity_unique t)
  runTest args (_test_cat_nil_list_nil t)

Main:

main_cat :: IO ()
main_cat = do
  _test_cat 20 100 (nil :: ConsList Bool)
  _test_cat 20 100 (nil :: ConsList Unary)