-- | @atLength atLen atEnd ls n@ unravels list @ls@ to position @n@. Precisely:
--
-- @
--  atLength atLenPred atEndPred ls n
--   | n < 0         = atLenPred ls
--   | length ls < n = atEndPred (n - length ls)
--   | otherwise     = atLenPred (drop n ls)
-- @
atLength :: ([a] -> b)   -- Called when length ls >= n, passed (drop n ls)
                         --    NB: arg passed to this function may be []
         -> b            -- Called when length ls <  n
         -> [a]
         -> Int
         -> b
atLength atLenPred atEnd ls0 n0
  | n0 < 0    = atLenPred ls0
  | otherwise = go n0 ls0
  where
    -- go's first arg n >= 0
    go 0 ls     = atLenPred ls
    go _ []     = atEnd           -- n > 0 here
    go n (_:xs) = go (n-1) xs

-- Some special cases of atLength:

-- | @(lengthExceeds xs n) = (length xs > n)@
atLength :: ([a] -> b)   -- Called when length ls >= n, passed (drop n ls)
                         --    NB: arg passed to this function may be []
         -> b            -- Called when length ls <  n
         -> [a]
         -> Int
         -> b
atLength atLenPred atEnd ls0 n0
  | n0 < 0    = atLenPred ls0
  | otherwise = go n0 ls0
  where
    -- go's first arg n >= 0
    go 0 ls     = atLenPred ls
    go _ []     = atEnd           -- n > 0 here
    go n (_:xs) = go (n-1) xs

-- Some special cases of atLength:

-- | @(lengthExceeds xs n) = (length xs > n)@
atLength atLenPred atEnd ls0 n0
  | n0 < 0    = atLenPred ls0
  | otherwise = go n0 ls0
  where
    -- go's first arg n >= 0
    go 0 ls     = atLenPred ls
    go _ []     = atEnd           -- n > 0 here
    go n (_:xs) = go (n-1) xs

-- Some special cases of atLength:

-- | @(lengthExceeds xs n) = (length xs > n)@