module Data.List.Base where

The type of lists🔗

This module contains the definition of the type of lists, and some basic operations on lists. Properties of these operations are in the module Data.List, not here.

data List {} (A : Type ) : Type  where
  [] : List A
  _∷_ : A  List A  List A

{-# BUILTIN LIST List #-}

One of the first things we set up is list literal syntax (the From-product class) for lists. The list corresponding to an n-ary product is the list of its elements.

instance
  From-prod-List : From-product A  _  List A)
  From-prod-List .From-product.from-prod = go where
    go :  n  Vecₓ A n  List A
    go zero xs                = []
    go (suc zero) xs          = xs  []
    go (suc (suc n)) (x , xs) = x  go (suc n) xs

-- Test:
_ : Path (List Nat) [ 1 , 2 , 3 ] (1  2  3  [])
_ = refl

“Fields”🔗

head : A  List A  A
head def []     = def
head _   (x  _) = x

tail : List A  List A
tail []       = []
tail (_  xs) = xs

Elimination🔗

As with any inductive type, the type of lists-of-As has a canonically-defined eliminator, but it also has some fairly popular recursors, called “folds”:

List-elim
  :  { ℓ'} {A : Type } (P : List A  Type ℓ')
   P []
   (∀ x xs  P xs  P (x  xs))
    x  P x
List-elim P p[] p∷ []       = p[]
List-elim P p[] p∷ (x  xs) = p∷ x xs (List-elim P p[] p∷ xs)

Functorial action🔗

It’s also possible to lift a function A → B to a function List A → List B.

instance
  Map-List : Map (eff List)
  Map-List = record { map = go } module map where
    go : (A  B)  List A  List B
    go f []       = []
    go f (x  xs) = f x  go f xs

{-# DISPLAY map.go f xs = map f xs #-}

Monoidal structure🔗

We can define concatenation of lists by recursion:

_++_ :  {} {A : Type }  List A  List A  List A
[]       ++ ys = ys
(x  xs) ++ ys = x  (xs ++ ys)

infixr 8 _++_

instance
  Append-List :  {} {A : Type }  Append (List A)
  Append-List = record { mempty = [] ; _<>_ = _++_ }
map-up : (Nat  A  B)  Nat  List A  List B
map-up f _ []       = []
map-up f n (x  xs) = f n x  map-up f (suc n) xs

length : List A  Nat
length []       = zero
length (x  xs) = suc (length xs)

concat : List (List A)  List A
concat [] = []
concat (x  xs) = x ++ concat xs

count : Nat  List Nat
count zero = []
count (suc n) = 0  map suc (count n)

product : List Nat  Nat
product [] = 1
product (x  xs) = x * product xs

module reverse where
  go : List A  List A  List A
  go acc [] = acc
  go acc (x  xs) = go (x  acc) xs

  go-β : (acc xs prx : List A)  go (prx ++ acc) xs  go prx xs ++ acc
  go-β acc []       prx = refl
  go-β acc (x  xs) prx = go-β acc xs (x  prx)

reverse : List A  List A
reverse = reverse.go []

reverse' : List A  List A
reverse' []       = []
reverse' (x  xs) = reverse' xs ++ (x  [])

reverse-β : (xs : List A)  reverse xs  reverse' xs
reverse-β [] = refl
reverse-β (x  xs) = reverse.go-β (x  []) xs []  ap₂ _++_ (reverse-β xs) refl

_∷r_ : List A  A  List A
xs ∷r x = xs ++ (x  [])

infixl 20 _∷r_

all=? : (A  A  Bool)  List A  List A  Bool
all=? eq=? [] [] = true
all=? eq=? [] (x  ys) = false
all=? eq=? (x  xs) [] = false
all=? eq=? (x  xs) (y  ys) = and (eq=? x y) (all=? eq=? xs ys)

enumerate :  {} {A : Type }  List A  List (Nat × A)
enumerate = go 0 where
  go : Nat  List _  List (Nat × _)
  go x [] = []
  go x (a  b) = (x , a)  go (suc x) b

take :  {} {A : Type }  Nat  List A  List A
take 0       xs       = []
take (suc n) []       = []
take (suc n) (x  xs) = x  take n xs

drop :  {} {A : Type }  Nat  List A  List A
drop zero    xs       = xs
drop (suc n) []       = []
drop (suc n) (x  xs) = drop n xs

split-at :  {} {A : Type }  Nat  List A  List A × List A
split-at 0       xs       = [] , xs
split-at (suc n) []       = [] , []
split-at (suc n) (x  xs) = ×-map₁ (x ∷_) (split-at n xs)

span :  {} {A : Type } (p : A  Bool)  List A  List A × List A
span p [] = [] , []
span p (x  xs) with p x
... | true  = ×-map₁ (x ∷_) (span p xs)
... | false = [] , x  xs

filter :  {} {A : Type } (p : A  Bool)  List A  List A
filter p [] = []
filter p (x  xs) with p x
... | true  = x  filter p xs
... | false = filter p xs

intercalate :  {} {A : Type } (x : A) (xs : List A)  List A
intercalate x []           = []
intercalate x (y  [])     = y  []
intercalate x (y  z  xs) = y  x  intercalate x (z  xs)

zip :  { ℓ'} {A : Type } {B : Type ℓ'}  List A  List B  List (A × B)
zip [] _ = []
zip _ [] = []
zip (a  as) (b  bs) = (a , b)  zip as bs

unzip :  { ℓ'} {A : Type } {B : Type ℓ'}  List (A × B)  List A × List B
unzip [] = [] , []
unzip ((a , b)  xs) = ×-map (a ∷_) (b ∷_) (unzip xs)

sigma :  { ℓ'} {A : Type } {B : A  Type ℓ'}  List A  (∀ a  List (B a))  List (Σ A B)
sigma [] f = []
sigma (x  xs) f = map (x ,_) (f x) <> sigma xs f

instance
  Idiom-List : Idiom (eff List)
  Idiom-List .pure a = a  []
  Idiom-List ._<*>_ f a = concat ((_<$> a) <$> f)

  Bind-List : Bind (eff List)
  Bind-List ._>>=_ a f = concat (f <$> a)

  Alt-List : Alt (eff List)
  Alt-List .Alt.fail  = []
  Alt-List .Alt._<|>_ = _<>_

Predicates🔗

We can define a function that determines if a boolean-valued predicate holds on every element of a list.

all-of :  {} {A : Type }  (A  Bool)  List A  Bool
all-of f [] = true
all-of f (x  xs) = and (f x) (all-of f xs)

Dually, we can also define a function that determines if a boolean-valued predicate holds on some element of a list.

any-of :  {} {A : Type }  (A  Bool)  List A  Bool
any-of f [] = false
any-of f (x  xs) = or (f x) (any-of f xs)
∷-head-inj :  {x y : A} {xs ys}  (x  xs)  (y  ys)  x  y
∷-head-inj {x = x} p = ap (head x) p

∷-tail-inj :  {x y : A} {xs ys}  (x  xs)  (y  ys)  xs  ys
∷-tail-inj p = ap tail p

∷≠[] :  {x : A} {xs}  ¬ (x  xs)  []
∷≠[] {A = A} p = subst distinguish p tt where
  distinguish : List A  Type
  distinguish []     = 
  distinguish (_  _) = 

[]≠∷ :  {x : A} {xs}  ¬ []  (x  xs)
[]≠∷ {A = A} p = ∷≠[] (sym p)

instance
  Discrete-List :   d : Discrete A   Discrete (List A)
  Discrete-List .decide = go where
    go :  x y  Dec (x  y)
    go []     []         = yes refl
    go []     (x  y)    = no λ p  ∷≠[] (sym p)
    go (x  xs) []       = no ∷≠[]
    go (x  xs) (y  ys) = case x ≡? y of λ where
      (yes x=y)  case go xs ys of λ where
        (yes xs=ys)  yes (ap₂ _∷_ x=y xs=ys)
        (no  xs≠ys)  no λ p  xs≠ys (∷-tail-inj p)
      (no x≠y)       no λ p  x≠y (∷-head-inj p)

traverse-up
  :  {M : Effect}  _ : Idiom M  (let module M = Effect M) { ℓ'}
    {a : Type } {b : Type ℓ'}
   (Nat  a  M.₀ b)  Nat  List a  M.₀ (List b)
traverse-up f n xs = sequence (map-up f n xs)

lookup :  _ : Discrete A   A  List (A × B)  Maybe B
lookup x [] = nothing
lookup x ((k , v)  xs) with x ≡? k
... | yes _ = just v
... | no  _ = lookup x xs

_!_ : (l : List A)  Fin (length l)  A
(x  xs) ! n with fin-view n
... | zero  = x
... | suc i = xs ! i

tabulate :  {n} (f : Fin n  A)  List A
tabulate {n = zero}  f = []
tabulate {n = suc n} f = f fzero  tabulate (f  fsuc)