A straight-forward inductive definition of non-empty lists akin to the usual
list: a non-empty list is either a singleton or a cons that has a head and a
tail.
Inductive ne_list (A : Type) : Type := | nel_singl : A -> ne_list A | nel_cons : A -> ne_list A -> ne_list A. Arguments nel_singl {_} _ : assert. Arguments nel_cons {_} _ _ : assert. Infix ":::" := nel_cons (at level 60, right associativity). Definition ne_list_hd {A} (l : ne_list A) : A := match l with | nel_singl a => a | nel_cons a _ => a end. Definition ne_list_tl {A} (l : ne_list A) : option (ne_list A) := match l with | nel_singl _ => None | nel_cons _ tl => Some tl end. Definition ne_list_foldr {A B} (f : A -> B -> B) (b : B) (l : ne_list A) := (fix fold (l : ne_list A) := match l with | nel_singl a => f a b | nel_cons a tl => f a (fold tl) end) l. Definition ne_list_app {A} (l1 l2 : ne_list A) := ne_list_foldr nel_cons l2 l1. #[export] Instance ne_list_ret : MRet ne_list := @nel_singl. #[export] Instance ne_list_bind : MBind ne_list := fun A B f l => (fix bind (l : ne_list A) := match l with | nel_singl a => f a | nel_cons a tl => ne_list_app (f a) (bind tl) end) l. #[export] Instance ne_list_fmap : FMap ne_list := fun A B f => mbind (mret ∘ f). #[export] Instance ne_list_join : MJoin ne_list := fun A => mbind id. Inductive ne_list_equiv `{Equiv A} : Equiv (ne_list A) := | ne_one_equiv x y : x ≡ y -> nel_singl x ≡ nel_singl y | ne_cons_equiv x y l k : x ≡ y -> l ≡ k -> x ::: l ≡ y ::: k. Definition ne_list_to_list {A} (nel : ne_list A) : list A := ne_list_foldr cons [] nel. Definition ne_list_length {A} (nel : ne_list A) := length (ne_list_to_list nel).A: Type
nel: ne_list A1 ≤ ne_list_length nelby destruct nel; cbn; lia. Qed.A: Type
nel: ne_list A1 ≤ ne_list_length nelA: Type
a: A
tl: ne_list Ane_list_to_list (a ::: tl) = a :: ne_list_to_list tldone. Qed. Definition ne_list_to_list_tl {A} (l : ne_list A) : list A := match l with | nel_singl _ => [] | nel_cons _ tl => ne_list_to_list tl end. Definition ne_list_option_cons {A} (a : A) (mnel : option (ne_list A)) : ne_list A := from_option (nel_cons a) (nel_singl a) mnel. Definition list_to_option_ne_list {A} (l : list A) : option (ne_list A) := foldr (fun a mnel => Some (ne_list_option_cons a mnel)) None l.A: Type
a: A
tl: ne_list Ane_list_to_list (a ::: tl) = a :: ne_list_to_list tlA: Type
a: A
l: list Alist_to_option_ne_list (a :: l) = Some (ne_list_option_cons a (list_to_option_ne_list l))done. Qed.A: Type
a: A
l: list Alist_to_option_ne_list (a :: l) = Some (ne_list_option_cons a (list_to_option_ne_list l))
A definition of non-empty lists based on lists.
Record NeList (A : Type) : Type := { nl_hd : A; nl_tl : list A; }. Arguments nl_hd {_} _ : assert. Arguments nl_tl {_} _ : assert. Definition ne_list_to_NeList {A} (l : ne_list A) : NeList A := {| nl_hd := ne_list_hd l; nl_tl := ne_list_to_list_tl l; |}. Definition NeList_to_ne_list {A} (l : NeList A) : ne_list A := ne_list_option_cons (nl_hd l) (list_to_option_ne_list (nl_tl l)).A: Type
a, b: A
l: list ANeList_to_ne_list {| nl_hd := a; nl_tl := b :: l |} = a ::: NeList_to_ne_list {| nl_hd := b; nl_tl := l |}done. Qed.A: Type
a, b: A
l: list ANeList_to_ne_list {| nl_hd := a; nl_tl := b :: l |} = a ::: NeList_to_ne_list {| nl_hd := b; nl_tl := l |}A: Type∀ l : NeList A, ne_list_to_list (NeList_to_ne_list l) = nl_hd l :: nl_tl lA: Type∀ l : NeList A, ne_list_to_list (NeList_to_ne_list l) = nl_hd l :: nl_tl lby rewrite NeList_to_ne_list_unroll, ne_list_to_list_unroll, IHt. Qed.A: Type
a: A
t: list A
IHt: ∀ h : A, ne_list_to_list (NeList_to_ne_list {| nl_hd := h; nl_tl := t |}) = nl_hd {| nl_hd := h; nl_tl := t |} :: nl_tl {| nl_hd := h; nl_tl := t |}
h: Ane_list_to_list (NeList_to_ne_list {| nl_hd := h; nl_tl := a :: t |}) = nl_hd {| nl_hd := h; nl_tl := a :: t |} :: nl_tl {| nl_hd := h; nl_tl := a :: t |}A: Type∀ l : NeList A, ne_list_to_NeList (NeList_to_ne_list l) = lA: Type∀ l : NeList A, ne_list_to_NeList (NeList_to_ne_list l) = lA: Type
h1, h2: A
t: list Ane_list_to_NeList (NeList_to_ne_list {| nl_hd := h1; nl_tl := h2 :: t |}) = {| nl_hd := h1; nl_tl := h2 :: t |}A: Type
h1, h2: A
t: list Ane_list_to_NeList (h1 ::: NeList_to_ne_list {| nl_hd := h2; nl_tl := t |}) = {| nl_hd := h1; nl_tl := h2 :: t |}by apply NeList_to_ne_list_to_list. Qed.A: Type
h1, h2: A
t: list Ane_list_to_list_tl (h1 ::: NeList_to_ne_list {| nl_hd := h2; nl_tl := t |}) = h2 :: tA: Type∀ l : ne_list A, list_to_option_ne_list (ne_list_to_list l) = Some lA: Type∀ l : ne_list A, list_to_option_ne_list (ne_list_to_list l) = Some lA: Type
a: A
l: ne_list A
IHl: list_to_option_ne_list (ne_list_to_list l) = Some llist_to_option_ne_list (ne_list_to_list (a ::: l)) = Some (a ::: l)by rewrite IHl. Qed.A: Type
a: A
l: ne_list A
IHl: list_to_option_ne_list (ne_list_to_list l) = Some lSome (ne_list_option_cons a (list_to_option_ne_list (ne_list_to_list l))) = Some (a ::: l)A: Type∀ l : ne_list A, NeList_to_ne_list (ne_list_to_NeList l) = lA: Type∀ l : ne_list A, NeList_to_ne_list (ne_list_to_NeList l) = lA: Type
a: A
n: ne_list ANeList_to_ne_list (ne_list_to_NeList (a ::: n)) = a ::: nby rewrite ne_list_to_list_to_nelist. Qed. #[export] Instance elem_of_ne_list {A} : ElemOf A (ne_list A) := fun a nel => a ∈ ne_list_to_list nel. #[export] Instance ne_list_subseteq {A} : SubsetEq (ne_list A) := fun l1 l2 => forall x, x ∈ l1 -> x ∈ l2.A: Type
a: A
n: ne_list Ane_list_option_cons a (list_to_option_ne_list (ne_list_to_list n)) = a ::: nA: Type
dec: EqDecision ARelDecision elem_ofA: Type
dec: EqDecision ARelDecision elem_ofA: Type
dec: EqDecision A
a: A
l: ne_list ADecision (a ∈ l)by typeclasses eauto. Qed. Definition ne_list_from_non_empty_list {A} (l : list A) : l <> [] -> ne_list A := match l with | [] => fun H => False_rect _ (H eq_refl) | a :: l' => fun _ => NeList_to_ne_list {| nl_hd := a; nl_tl := l' |} end. Definition list_function_restriction {A B} (f : A -> list B) (da : dsig (fun a => f a <> [])) : ne_list B := ne_list_from_non_empty_list (f (` da)) (proj2_dsig da).A: Type
dec: EqDecision A
a: A
l: ne_list ADecision (a ∈ ne_list_to_list l)A, B: Type
f: A → list B
l: list Amjoin (map ne_list_to_list (list_filter_map (λ a : A, f a ≠ []) (list_function_restriction f) l)) = l ≫= fA, B: Type
f: A → list B
l: list Amjoin (map ne_list_to_list (list_filter_map (λ a : A, f a ≠ []) (list_function_restriction f) l)) = l ≫= fA, B: Type
f: A → list B
x: A
l: list A
IHl: mjoin (map ne_list_to_list (list_filter_map (λ a : A, f a ≠ []) (list_function_restriction f) l)) = l ≫= fmjoin (map ne_list_to_list (list_filter_map (λ a : A, f a ≠ []) (list_function_restriction f) (l ++ [x]))) = (l ++ [x]) ≫= fA, B: Type
f: A → list B
x: A
l: list A
IHl: mjoin (map ne_list_to_list (list_filter_map (λ a : A, f a ≠ []) (list_function_restriction f) l)) = l ≫= f(l ≫= f) ++ mjoin (map ne_list_to_list (list_filter_map (λ a : A, f a ≠ []) (list_function_restriction f) [x])) = (l ≫= f) ++ [x] ≫= fA, B: Type
f: A → list B
x: A
l: list A(l ≫= f) ++ mjoin (map ne_list_to_list (map (list_function_restriction f) match decide (f x ≠ []) with | left p => [dexist x p] | right _ => [] end)) = (l ≫= f) ++ f x ++ []A, B: Type
f: A → list B
x: A
l: list Amjoin (map ne_list_to_list (map (list_function_restriction f) match decide (f x ≠ []) with | left p => [dexist x p] | right _ => [] end)) = f xA, B: Type
f: A → list B
x: A
l: list A
Hfx: f x ≠ []ne_list_to_list (list_function_restriction f (dexist x Hfx)) ++ [] = f xA, B: Type
f: A → list B
x: A
l: list A
Hfx: f x ≠ []ne_list_to_list (match f (`(dexist x Hfx)) as l return (l ≠ [] → ne_list B) with | [] => λ H : [] ≠ [], False_rect (ne_list B) (H eq_refl) | a :: l' => λ _ : a :: l' ≠ [], NeList_to_ne_list {| nl_hd := a; nl_tl := l' |} end (proj2_dsig (dexist x Hfx))) ++ [] = f xA, B: Type
f: A → list B
x: A
l: list A
Hfx, Hp: f x ≠ []ne_list_to_list (match f (`(dexist x Hfx)) as l return (l ≠ [] → ne_list B) with | [] => λ H : [] ≠ [], False_rect (ne_list B) (H eq_refl) | a :: l' => λ _ : a :: l' ≠ [], NeList_to_ne_list {| nl_hd := a; nl_tl := l' |} end Hp) ++ [] = f xA, B: Type
f: A → list B
x: A
l: list A
Hfx, Hp: f x ≠ []ne_list_to_list (match f x as l return (l ≠ [] → ne_list B) with | [] => λ H : [] ≠ [], False_rect (ne_list B) (H eq_refl) | a :: l' => λ _ : a :: l' ≠ [], NeList_to_ne_list {| nl_hd := a; nl_tl := l' |} end Hp) ++ [] = f xby rewrite NeList_to_ne_list_to_list, app_nil_r. Qed.A, B: Type
f: A → list B
x: A
l: list A
b: B
l0: list B
Hfx, Hp: b :: l0 ≠ []ne_list_to_list (NeList_to_ne_list {| nl_hd := b; nl_tl := l0 |}) ++ [] = b :: l0A: Type
l: list (ne_list A)length (mjoin (map ne_list_to_list l)) ≥ length lA: Type
l: list (ne_list A)length (mjoin (map ne_list_to_list l)) ≥ length lA: Type
a: ne_list A
l: list (ne_list A)
IHl: length (mjoin (map ne_list_to_list l)) ≥ length llength (ne_list_to_list a ++ mjoin (map ne_list_to_list l)) ≥ S (length l)(* by specialize (ne_list_min_length a); unfold ne_list_length; lia. *) (* for some strange reason the line above does not work, so... this hack: *)A: Type
a: ne_list A
l: list (ne_list A)
IHl: length (mjoin (map ne_list_to_list l)) ≥ length llength (ne_list_to_list a) + length (mjoin (map ne_list_to_list l)) ≥ S (length l)by apply Hle; [apply ne_list_min_length |]. Qed.A: Type
a: ne_list A
l: list (ne_list A)
IHl: length (mjoin (map ne_list_to_list l)) ≥ length l
Hle: ∀ a b c : nat, a ≥ 1 → b ≥ c → a + b ≥ S clength (ne_list_to_list a) + length (mjoin (map ne_list_to_list l)) ≥ S (length l)
An alternative inductive definition of non-empty lists as a record which
has a head and an optional tail.
Inductive NonEmptyList (A : Type) : Type := NEL_cons { nel_hd : A; nel_tl : option (NonEmptyList A) }. Arguments nel_hd {_} _ : assert. Arguments nel_tl {_} _ : assert. Arguments NEL_cons {_} _ _ : assert. #[export] Instance NonEmptyList_inhabited `{Inhabited A} : Inhabited (NonEmptyList A) := populate (NEL_cons inhabitant None). Definition NEL_singl `(a : A) : NonEmptyList A := NEL_cons a None. Fixpoint NonEmptyList_ind' (A : Type) (P : NonEmptyList A -> Prop) (hd' : forall h : A, P (NEL_cons h None)) (tl' : forall (h : A) (t : NonEmptyList A), P t -> P (NEL_cons h (Some t))) (l : NonEmptyList A) {struct l} : P l := match l with | NEL_cons h None => hd' h | NEL_cons h (Some t) => tl' h t (NonEmptyList_ind' A P hd' tl' t) end. Fixpoint NonEmptyList_to_ne_list `(l : NonEmptyList A) : ne_list A := match nel_tl l with | None => nel_singl (nel_hd l) | Some l' => nel_cons (nel_hd l) (NonEmptyList_to_ne_list l') end. Fixpoint ne_list_to_NonEmptyList `(l : ne_list A) : NonEmptyList A := match l with | nel_singl a => NEL_singl a | nel_cons a l' => NEL_cons a (Some (ne_list_to_NonEmptyList l')) end.A: Type
l: NonEmptyList Ane_list_to_NonEmptyList (NonEmptyList_to_ne_list l) = lby induction l using NonEmptyList_ind'; cbn; [| rewrite IHl]. Qed.A: Type
l: NonEmptyList Ane_list_to_NonEmptyList (NonEmptyList_to_ne_list l) = lA: Type
l: ne_list ANonEmptyList_to_ne_list (ne_list_to_NonEmptyList l) = lby induction l; cbn; [| rewrite IHl]. Qed.A: Type
l: ne_list ANonEmptyList_to_ne_list (ne_list_to_NonEmptyList l) = l