At

Posted on 2017-04-28 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 At
  ( at, _test_at, main_at
  ) where

import Testing
import Booleans
import DisjointUnions
import NaturalNumbers
import BailoutRecursion
import Plus
import LessThanOrEqualTo
import Lists
import HeadAndTail
import Snoc
import Reverse
import Cat
import Length

The \(\head\) function attempts to extract the “first” item in a list; in this post we’ll generalize to \(\at\), which extracts the element at an arbitrary position in a list.

Define \(\beta : \nats \times \lists{A} \rightarrow \bool\) by \[\beta(k,x) = \isnil(x),\] \(\psi : \nats \times \lists{A} \rightarrow 1 + A\) by \[\psi(k,x) = \lft(\ast),\] and \(\omega : \nats \times \lists{A} \rightarrow \lists{A}\) by \[\omega(k,x) = \tail(x).\] We then define \(\at : \lists{A} \times \nats \rightarrow \ast + A\) by \[\at(x,k) = \bailrec(\head)(\beta)(\psi)(\omega)(k,x).\]

In Haskell:

Since \(\at\) is defined in terms of \(\bailrec\), it is the unique solution to a system of functional equations.

\(\at\) is the unique map \(f : \nats \times \lists{A} \rightarrow 1 + A\) such that the following hold for all \(n \in \nats\), \(a \in A\), and \(x \in \lists{A}\). \[\left\{\begin{array}{l} f(x,\zero) = \head(x) \\ f(x,\next(n)) = \bif{\isnil(x)}{\lft(\ast)}{f(\tail(x),n)} \end{array}\right.\]

\(\at\) interacts with \(\cons\) and \(\next\).

Let \(A\) be a set. For all \(a \in A\), \(x \in \lists{A}\), and \(k \in \nats\), we have \[\at(\cons(a,x),\next(k)) = \at(x,k).\]

Note that \[\begin{eqnarray*} & & \at(\cons(a,x),\next(k)) \\ & \href{/posts/arithmetic-made-difficult/At.html#cor-at-next} = & \bif{\isnil(\cons(a,x))}{\lft(\ast)}{\at(\tail(\cons(a,x)),k)} \\ & \href{/posts/arithmetic-made-difficult/HeadAndTail.html#thm-isnil-cons} = & \bif{\bfalse}{\lft(\ast)}{\at(\tail(\cons(a,x)),k)} \\ & \href{/posts/arithmetic-made-difficult/Booleans.html#cor-if-false} = & \at(\tail(\cons(a,x)),k) \\ & \href{/posts/arithmetic-made-difficult/HeadAndTail.html#thm-tail-cons} = & \at(x,k) \end{eqnarray*}\] as claimed.

Let’s trace some special cases.

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

  1. \(\at(\nil,k) = \lft(\ast)\).
  2. \(\at(\cons(a,x),\zero) = \rgt(a)\).
  3. \(\at(\cons(a,\cons(b,x)),\next(\zero)) = \rgt(b)\).
  1. We consider two cases: \(k = \zero\) and \(k \neq \zero\). If \(k = \zero\), we of course have \[\begin{eqnarray*} & & \at(\nil,\zero) \\ & \href{/posts/arithmetic-made-difficult/At.html#thm-at-nil} = & \lft(\ast) \end{eqnarray*}\] as claimed. If \(k = \next(m)\), we have \[\begin{eqnarray*} & & \at(\nil,\next(m)) \\ & \href{/posts/arithmetic-made-difficult/At.html#cor-at-next} = & \bif{\isnil(\nil)}{\lft(\ast)}{\at(\tail(\nil),m)} \\ & \href{/posts/arithmetic-made-difficult/HeadAndTail.html#thm-isnil-nil} = & \bif{\btrue}{\lft(\ast)}{\at(\tail(\nil),m)} \\ & \href{/posts/arithmetic-made-difficult/Booleans.html#cor-if-true} = & \lft(\ast) \end{eqnarray*}\] as claimed.
  2. We have \[\begin{eqnarray*} & & \at(\cons(a,x),\zero) \\ & \href{/posts/arithmetic-made-difficult/At.html#cor-at-zero} = & \head(\cons(a,x)) \\ & \href{/posts/arithmetic-made-difficult/HeadAndTail.html#thm-head-cons} = & \rgt(a) \end{eqnarray*}\] as claimed.
  3. We have \[\begin{eqnarray*} & & \at(\cons(a,\cons(b,x)),\next(\zero)) \\ & \href{/posts/arithmetic-made-difficult/At.html#thm-at-cons-next} = & \at(\cons(b,x),\zero) \\ & \href{/posts/arithmetic-made-difficult/At.html#thm-at-cons-zero} = & \rgt(b) \end{eqnarray*}\] as claimed.

\(\at\) interacts with \(\length\).

Let \(A\) be a set and let \(z \in \lists{A}\).

  1. \(\at(\cons(a,x),\length(x)) = \head(\rev(\cons(a,x)))\).
  2. \(\at(x,\length(x)) = \lft(\ast)\).
  1. We proceed by list induction on \(x\). For the base case \(x = \nil\), we have \[\begin{eqnarray*} & & \at(\cons(a,\nil),\length(\nil)) \\ & \href{/posts/arithmetic-made-difficult/Length.html#cor-length-nil} = & \at(\cons(a,\nil),\zero) \\ & = & \rgt(a) \\ & \href{/posts/arithmetic-made-difficult/HeadAndTail.html#thm-head-cons} = & \head(\cons(a,\nil)) \\ & = & \head(\rev(\cons(a,\nil))) \end{eqnarray*}\] as needed. For the inductive step, suppose the implication holds for all \(a\) for some \(x\), and let \(b \in A\). Now \[\begin{eqnarray*} & & \at(\cons(a,\cons(b,x)),\length(\cons(b,x))) \\ & \href{/posts/arithmetic-made-difficult/Length.html#cor-length-cons} = & \at(\cons(a,\cons(b,x)),\next(\length(x))) \\ & = & \at(\cons(b,x),length(x)) \\ & = & \head(\rev(\cons(b,x))) \\ & = & \head(\snoc(a,\rev(\cons(b,x)))) \\ & \href{/posts/arithmetic-made-difficult/Reverse.html#cor-rev-cons} = & \head(\rev(\cons(a,\cons(b,x)))) \end{eqnarray*}\] as needed.
  2. We proceed by list induction on \(x\). For the base case \(x = \nil\), we have \[\begin{eqnarray*} & & \at(\nil,\length(\nil)) \\ & \href{/posts/arithmetic-made-difficult/At.html#thm-at-nil} = & \lft(\ast) \end{eqnarray*}\] as needed. For the inductive step, suppose the equality holds for some \(x\), and let \(a \in A\). Now \[\begin{eqnarray*} & & \at(\cons(a,x),\length(\cons(a,x))) \\ & \href{/posts/arithmetic-made-difficult/Length.html#cor-length-cons} = & \at(\cons(a,x),\next(\length(x))) \\ & = & \at(x,\length(x)) \\ & \href{/posts/arithmetic-made-difficult/At.html#thm-at-length} = & \lft(\ast) \end{eqnarray*}\] as needed.

\(\at\) interacts with \(\snoc\).

Let \(A\) be a set. Let \(a,b \in A\) and \(x \in \lists{A}\).

  1. If \(\nleq(k,\length(x))\) then \(\at(\snoc(a,\cons(b,x)),k) = \at(\cons(b,x),k)\).
  2. \(\at(\snoc(a,x),\length(x)) = \rgt(a)\).
  1. We proceed by induction on \(k\). For the base case \(k = \zero\), note that \(\nleq(k,\length(x))\), and we have \[\begin{eqnarray*} & & \at(\snoc(a,\cons(b,x)),\zero) \\ & \href{/posts/arithmetic-made-difficult/Snoc.html#cor-snoc-cons} = & \at(\cons(b,\snoc(a,x)),\zero) \\ & \href{/posts/arithmetic-made-difficult/At.html#thm-at-cons-zero} = & \rgt(b) \\ & \href{/posts/arithmetic-made-difficult/At.html#thm-at-cons-zero} = & \at(\cons(b,x),\zero) \end{eqnarray*}\] as needed. For the inductive step, suppose the implication holds for all \(x\), \(a\), and \(b\) for some \(k\), and suppose further that \(\nleq(\next(k),\length(x))\). In particular, we must have \(x = \cons(c,u)\) for some \(c \in A\) and \(u \in \lists{A}\), and \(\nleq(k,\length(u))\). Now we have \[\begin{eqnarray*} & & \at(\snoc(a,\cons(b,x)),\next(k)) \\ & \href{/posts/arithmetic-made-difficult/Snoc.html#cor-snoc-cons} = & \at(\cons(b,\snoc(a,x)),\next(k)) \\ & \href{/posts/arithmetic-made-difficult/At.html#thm-at-cons-next} = & \at(\snoc(a,x),k) \\ & \hyp{x = \cons(c,u)} = & \at(\snoc(a,\cons(c,u)),k) \\ & = & \at(\cons(c,u),k) \\ & \hyp{x = \cons(c,u)} = & \at(x,k) \\ & \href{/posts/arithmetic-made-difficult/At.html#thm-at-cons-next} = & \at(\cons(b,x),\next(k)) \end{eqnarray*}\] as needed.
  2. We proceed by list induction on \(x\). For the base case \(x = \nil\), we have \[\begin{eqnarray*} & & \at(\snoc(a,\nil),\length(\nil)) \\ & \href{/posts/arithmetic-made-difficult/Length.html#cor-length-nil} = & \at(\snoc(a,\nil),\zero) \\ & \href{/posts/arithmetic-made-difficult/Snoc.html#cor-snoc-nil} = & \at(\cons(a,\nil),\zero) \\ & \href{/posts/arithmetic-made-difficult/At.html#thm-at-cons-zero} = & \rgt(a) \end{eqnarray*}\] as needed. For the inductive step, suppose the equality holds for all \(a\) for some \(x\), and let \(b \in A\). Now \[\begin{eqnarray*} & & \at(\snoc(a,\cons(b,x)),\length(\cons(b,x))) \\ & \href{/posts/arithmetic-made-difficult/Snoc.html#cor-snoc-cons} = & \at(\cons(b,\snoc(a,x)),\length(\cons(b,x))) \\ & \href{/posts/arithmetic-made-difficult/Length.html#cor-length-cons} = & \at(\cons(b,\snoc(a,x)),\next(\length(x))) \\ & \href{/posts/arithmetic-made-difficult/At.html#thm-at-cons-next} = & \at(\snoc(a,x),\length(x)) \\ & \hyp{\at(\snoc(a,x),\length(x)) = \rgt(a)} = & \rgt(a) \end{eqnarray*}\] as needed.

\(\at\) interacts with \(\rev\).

Let \(A\) be a set. Let \(x \in \lists{A}\) and \(u,v \in \nats\). If \(\next(\nplus(u,v)) = \length(x)\), then we have \[\at(\rev(x),v) = \at(x,u).\]

We proceed by list induction on \(x\). For the base case \(x = \nil\), note that \(\length(x) = \zero\), so that \(\next(\nplus(u,v)) = \length(x)\) is false for all \(u\) and \(v\), and thus the implication holds vacuously. For the inductive step, suppose the implication holds for all \(u\) and \(v\) for some \(x\) and let \(a \in A\). Suppose further that \(\next(\nplus(u,v)) = \length(\cons(a,x))\). We have two possibilities for \(u\). If \(u = \zero\), we have \(\next(v) = \length(\cons(a,x))\) and thus \(v = \length(x)\). In this case, we have \[\begin{eqnarray*} & & \at(\cons(a,x),u) \\ & \let{u = \zero} = & \at(\cons(a,x),\zero) \\ & \href{/posts/arithmetic-made-difficult/At.html#thm-at-cons-zero} = & \rgt(a) \\ & \href{/posts/arithmetic-made-difficult/At.html#thm-at-snoc-length} = & \at(\snoc(a,\rev(x)),\length(\rev(x))) \\ & \href{/posts/arithmetic-made-difficult/Length.html#thm-length-rev} = & \at(\snoc(a,\rev(x)),\length(x)) \\ & \href{/posts/arithmetic-made-difficult/Reverse.html#cor-rev-cons} = & \at(\rev(\cons(a,x)),\length(x)) \\ & \hyp{v = \length(x)} = & \at(\rev(\cons(a,x)),v) \end{eqnarray*}\] as needed. If \(u = \next(w)\), then we have \(\next(\nplus(w,v)) = \length(x)\). In particular, we have \(x \neq \nil\) and thus \(\rev(x) \neq \nil\); say \(\rev(x) = \cons(c,y)\). Now \(\nleq(v,\length(y))\), and moreover \[\begin{eqnarray*} & & \at(\cons(a,x),u) \\ & \let{u = \next(w)} = & \at(\cons(a,x),\next(w)) \\ & \href{/posts/arithmetic-made-difficult/At.html#thm-at-cons-next} = & \at(x,w) \\ & = & \at(\rev(x),v) \\ & = & \at(\cons(c,y),v) \\ & = & \at(\snoc(a,\cons(c,y)),v) \\ & = & \at(\snoc(a,\rev(x)),v) \\ & \href{/posts/arithmetic-made-difficult/Reverse.html#cor-rev-cons} = & \at(\rev(\cons(a,x)),v) \end{eqnarray*}\] as needed.

\(\at\) interacts with \(\cat\).

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

  1. If \(\nleq(k,\length(x))\), then \(\at(\cat(\cons(a,x),y),k) = \at(\cons(a,x),k)\).
  2. If \(\at(\cat(\cons(a,x),y),\nplus(\next(\length(x)),k)) = \at(y,k)\).
  1. We proceed by list induction on \(y\). For the base case \(y = \nil\), we have \[\begin{eqnarray*} & & \at(\cat(\cons(a,x),\nil),k) \\ & \href{/posts/arithmetic-made-difficult/Cat.html#thm-cat-nil-right} = & \at(\cons(a,x),k) \end{eqnarray*}\] as claimed. For the inductive step, suppose the implication holds for all \(k\), \(a\), and \(x\) for some \(y\), and let \(b \in A\), and suppose further that \(\nleq(k,\length(x))\). Using the inductive hypothesis we have \[\begin{eqnarray*} & & \at(\cat(\cons(a,x),\cons(b,y)),k) \\ & \href{/posts/arithmetic-made-difficult/Cat.html#thm-cat-snoc-left} = & \at(\cat(\snoc(b,\cons(a,x)),y),k) \\ & = & \at(\snoc(b,\cons(a,x)),k) \\ & = & \at(\cons(a,x),k) \end{eqnarray*}\] as needed.
  2. We proceed by list induction on \(x\). For the base case \(x = \nil\), we have \[\begin{eqnarray*} & & \at(\cat(\cons(a,\nil),y),\nplus(\next(\length(\nil)),k)) \\ & = & \at(\cons(a,\cat(\nil,y)),\next(\nplus(\zero,k))) \\ & = & \at(\cons(a,y),\next(k)) \\ & \href{/posts/arithmetic-made-difficult/At.html#thm-at-cons-next} = & \at(y,k) \end{eqnarray*}\] as needed. For the inductive step, suppose the equality holds for all \(a\), \(y\), and \(k\) for some \(x\), and let \(b \in A\). Note that \(\snoc(b,x) = \cons(c,u)\) for some \(c\) and u$, where \(\length(x) = \next(\length(u))\). Now \[\begin{eqnarray*} & & \at(\cat(\cons(a,x),\cons(b,y)),\nplus(\next(\length(x)),k)) \\ & = & \at(\cat(\snoc(b,\cons(a,x)),y),\next(\nplus(\length(x),k))) \\ & \href{/posts/arithmetic-made-difficult/Snoc.html#cor-snoc-cons} = & \at(\cat(\cons(a,\snoc(b,x)),y),\next(\nplus(\length(x),k))) \\ & = & \at(\cons(a,\cat(\snoc(b,x)),y),\next(\nplus(\length(x),k))) \\ & = & \at(\cat(\snoc(b,x),y),\nplus(\length(u),k)) \\ & = & \at(\cat(\snoc(b,x),y),\nplus(\next(\length(u)),k)) \\ & = & \at(\cat(\cons(c,u),y),\nplus(\next(\length(u)),k)) \\ & = & \at(\cons(b,y),k) \end{eqnarray*}\] as needed.

We can characterize the \(k\)s such that \(\at(x,k)\) is a \(\lft\) or a \(\rgt\).

Let \(A\) be a set. For all \(x \in \lists{A}\) and \(k \in \nats\) we have the following.

  1. If \(\nleq(\length(x),k)\), then \(\at(x,k) = \lft(\ast)\).
  2. If \(\nleq(k,\length(x))\), then \(\at(\cons(a,x),k) = \rgt(b)\) for some \(b \in A\).
  1. We proceed by list induction on \(x\). For the base case \(x = \nil\), note that for any \(k\) we have \(\nleq(\zero,k)\) and \[\begin{eqnarray*} & & \at(\nil,k) \\ & \href{/posts/arithmetic-made-difficult/At.html#thm-at-nil} = & \lft(\ast) \end{eqnarray*}\] as needed. For the inductive step, suppose the implication holds for all \(k\) for some \(x\) and let \(a \in A\). Suppose further that \(\nleq(\length(\cons(a,x),k))\); we then have \(k = \next(m)\) for some \(m\), where \(\nleq(\length(x),m)\). Now \[\begin{eqnarray*} & & \at(\cons(a,x),k) \\ & \hyp{k = \next(m)} = & \at(\cons(a,x),\next(m)) \\ & \href{/posts/arithmetic-made-difficult/At.html#thm-at-cons-next} = & \at(x,m) \\ & = & \lft(\ast) \end{eqnarray*}\] as needed.
  2. We proceed by list induction on \(x\). For the base case \(x = \nil\), note that if \(\nleq(k,\length(\nil))\) then \(k = \zero\). Now \[\begin{eqnarray*} & & \at(\cons(a,\nil),\zero) \\ & \href{/posts/arithmetic-made-difficult/At.html#thm-at-cons-zero} = & \rgt(a) \end{eqnarray*}\] as needed. For the inductive step, suppose the implication holds for all \(k\) and \(a\) for some \(x\) and let \(b \in A\). Suppose further that \(\nleq(k,\length(\cons(b,x)))\). We have two possibilities for \(k\). If \(k = \zero\), we have \[\begin{eqnarray*} & & \at(\cons(a,\cons(b,x)),\zero) \\ & \href{/posts/arithmetic-made-difficult/At.html#thm-at-cons-zero} = & \rgt(a) \end{eqnarray*}\] as needed. Suppose instead that \(k = \next(m)\). Now \(\nleq(m,\length(x))\), and we have \[\begin{eqnarray*} & & \at(\cons(a,\cons(b,x)),k) \\ & = & \at(\cons(a,\cons(b,x)),\next(m)) \\ & \href{/posts/arithmetic-made-difficult/At.html#thm-at-cons-next} = & \at(\cons(b,x),m) \\ & = & \rgt(c) \end{eqnarray*}\] for some \(c\) by the inductive hypothesis, as needed.

Finally, \(\at\) detects equality for lists.

Let \(A\) be a set, and let \(x,y \in \lists{A}\). Then \(x = y\) if and only if \(\at(x,k) = \at(y,k)\) for all \(k \in \nats\).

The “only if” direction is clear. We show the “if” part by list induction on \(x\). For the base case \(x = \nil\), suppose we have \(\at(x,k) = \at(y,k)\) for all \(k \in \nats\). If \(y = \cons(a,z)\) for some \(z\), then we have \[\begin{eqnarray*} & & \rgt(a) \\ & \href{/posts/arithmetic-made-difficult/At.html#thm-at-cons-zero} = & \at(\cons(a,z),\zero) \\ & = & \at(y,\zero) \\ & = & \at(x,\zero) \\ & = & \at(\nil,\zero) \\ & \href{/posts/arithmetic-made-difficult/At.html#thm-at-nil} = & \lft(\ast) \end{eqnarray*}\] a contradiction. So \(y = \nil = x\) as claimed.

For the inductive step, suppose the implication holds for all \(y\) for some \(x\), let \(a \in A\), and suppose we have \(\at(\cons(a,x),k) = \at(y,k)\) for all \(k \in \nats\). If \(y = \nil\), then again we have \[\begin{eqnarray*} & & \rgt(a) \\ & = & \at(\cons(a,x),\next(\zero)) \\ & = & \at(y,\next(\zero)) \\ & = & \at(\nil,\next(\zero)) \\ & \href{/posts/arithmetic-made-difficult/At.html#thm-at-nil} = & \lft(\ast) \end{eqnarray*}\] a contradiction. Say \(y = \cons(b,z)\). Now \[\begin{eqnarray*} & & \at(z,k) \\ & \href{/posts/arithmetic-made-difficult/At.html#thm-at-cons-next} = & \at(\cons(b,z),\next(k)) \\ & = & \at(y,\next(k)) \\ & = & \at(\cons(a,x),\next(k)) \\ & \href{/posts/arithmetic-made-difficult/At.html#thm-at-cons-next} = & \at(x,k) \end{eqnarray*}\] By the inductive hypothesis, \(x = z\), so that \(\cons(a,x) = y\) as needed.

Testing

Suite:

_test_at ::
  ( TypeName a, Show a, Equal a, Arbitrary a
  , TypeName n, Natural n, Equal n, Show n, Arbitrary n
  , TypeName (t a), List t, Equal (t a), Show (t a), Arbitrary (t a)
  ) => Int -> Int -> t a -> n -> IO ()
_test_at size cases t n = do
  testLabel2 "at" t n

  let args = testArgs size cases

  runTest args (_test_at_zero t n)
  runTest args (_test_at_next t n)
  runTest args (_test_at_next_next_cons t n)
  runTest args (_test_at_nil t n)
  runTest args (_test_at_single t n)
  runTest args (_test_at_double t n)
  runTest args (_test_at_length t n)
  runTest args (_test_at_next_length t n)
  runTest args (_test_at_snoc t n)
  runTest args (_test_at_snoc_length t n)
  runTest args (_test_at_rev t n)
  runTest args (_test_at_cat_left t n)
  runTest args (_test_at_cat_right t n)
  runTest args (_test_at_isleft t n)
  runTest args (_test_at_isright t n)
  runTest args (_test_at_eq t n)

Main:

main_at :: IO ()
main_at = do
  _test_at 20 100 (nil :: ConsList Bool)  (zero :: Unary)
  _test_at 20 100 (nil :: ConsList Unary) (zero :: Unary)