Zulip Chat Archive

Stream: Is there code for X?

Topic: list with at least one element


Jared green (Aug 04 2024 at 00:26):

so i can do this:

def theFun (c : ilist a) : v a :=
  match c with
  | [b]  => f b
  | (b :: as )  => g b (theFun as)

that would give "missing cases"if it were list

Eric Wieser (Aug 04 2024 at 00:32):

You could use a docs#Subtype:

abbrev IList (α) := {l : List α // l  []}

def theFun (c : IList Nat) : Nat :=
  match c with
  | [b], _⟩ => 0
  | b :: as, _⟩ => 1

edit: though this doesn't help for the recursive case

Jared green (Aug 04 2024 at 00:36):

is this not already in mathlib or the builtins?

Eric Wieser (Aug 04 2024 at 00:48):

I'm pretty sure it's not

Eric Wieser (Aug 04 2024 at 00:49):

Probably you will have a better time using a custom inductive than what I do above

Jared green (Aug 04 2024 at 00:51):

can that be done while also getting the already implemented functionality of list?

Eric Wieser (Aug 04 2024 at 01:01):

That functionality isn't going to come for free whatever you do. If you care about compilation, then my approach above will be fastest, and you have to deal with not being able to use match syntax for recursion

Eric Wieser (Aug 04 2024 at 01:02):

If you don't care about compilation, you can write a pair ofIList.toList and IList.mk l hl functions and use the inductive APi

Jared green (Aug 04 2024 at 01:03):

what do you mean 'if you care about compilation'

Eric Wieser (Aug 04 2024 at 01:07):

I mean "if you are building executable code, and want conversion between IList and List to be free"

Eric Wieser (Aug 04 2024 at 01:08):

If you use the inductive spelling without some very clever FFI hacks, the cost of the conversion would be O(n)

Jared green (Aug 04 2024 at 01:17):

i need it to run, and i need recursion. i dont know how to use ffi at all, though there should ideally be optimization in the compiler so the conversion doesnt actually occur, which is the least of my worries.

Jared green (Aug 04 2024 at 01:27):

specifically i need something like fold, which would look like this if i was to build it:

def fold (l : IList a) (f : a -> b -> b) (g: a  ->  b) : b
  match l  with
  | [d] => g d
  | (d :: ds) => f  d (fold ds f g)

Jared green (Aug 04 2024 at 01:37):

i also need to take things that come in the form of lists and use them in that way

Yury G. Kudryashov (Aug 04 2024 at 02:19):

See also docs#FreeSemigroup

Jared green (Aug 04 2024 at 22:09):

i went with inductive. got stuck on ofList

import Init.Prelude
import Init.Coe
import Mathlib.Data.List.Basic
variable {a : Type}
inductive IList a
  where
  | single : a -> IList a
  | cons : a -> IList a -> IList

def IList.ofList (l : {l : List // l  [] }) : IList a :=
  if h : List.length l.1 = 1
  then single (List.head l.1 l.2)
  else cons (List.head l.1 l.2) (IList.ofList List.tail l.1,
    by
    by_contra ha
    apply h
    have hi : List.length l.1.tail = 0 := by {
       rw [ha]
       simp
    }
    rw [List.length_tail] at hi
    apply Nat.eq_add_of_sub_eq at hi
    simp at hi
    exact hi
    simp
    by_contra hj
    simp at hj
    apply l.2
    exact hj  

i needed the if statement to complete the proof, but now i need a termination proof

Eric Wieser (Aug 04 2024 at 22:13):

Here's a simple version:

def IList.ofList (l : {l : List a // l  [] }) : IList a :=
  match l with
  | [x], _⟩ => .single x
  | x :: xs@(y :: ys), h => .cons x (IList.ofList xs, fun h => by aesop)

Eric Wieser (Aug 04 2024 at 22:16):

A slightly neater spelling might be

def IList.ofList? (l : List a) : Option (IList a) :=
  match l with
  | [] => none
  | x :: xs => some <|
    match IList.ofList? xs with
    | .none => .single x
    | .some ixs => .cons x ixs

Jared green (Aug 04 2024 at 22:32):

what other imports are needed for the first one?

Kyle Miller (Aug 04 2024 at 22:34):

Any reason not to do this?

def IList (α : Type*) := α × List α
def IList.toList {α : Type*} : IList α  List α := fun (x, xs) => x :: xs
def List.toIList {α : Type*} (l : List α) (h : l  []) : IList α :=
  match l, h with
  | x :: xs, _ => (x, xs)

Jared green (Aug 04 2024 at 22:38):

i was making toList and ofList as coercions with {l : List a // l ≠ [] } so that i can treat them as the same.

Eric Wieser (Aug 04 2024 at 22:39):

Any reason not to do this?

You lose the ability to pattern match on a .cons constructor (you can make .single work with @[match_pattern])

Kyle Miller (Aug 04 2024 at 22:42):

I think this would take some concrete use cases to evaluate, but that's true.

A big benefit to my IList is that you don't have to rebuild the List to convert it.

Eric Wieser (Aug 04 2024 at 22:44):

Arguably {l : List a // l ≠ [] } also has that property, and is likely to make things like (IList.map l f).toList = l.toList.map f defeq

Kyle Miller (Aug 04 2024 at 22:44):

Yeah, the subtype is likely the best version

Eric Wieser (Aug 04 2024 at 22:45):

But as you say, there are tradeoffs to be made here, informed by the concrete use case we don't know!

Eric Wieser (Aug 04 2024 at 22:46):

(I think custom match patterns would remove the need for some tradeoffs, but that sounds like a massive design challenge; especially if such a scheme were to be compatible with ~q matching!)

Jared green (Aug 04 2024 at 23:04):

in fact i have not implemented IList.map, mainly because my application doesnt call for it.


Last updated: May 02 2025 at 03:31 UTC