Zulip Chat Archive

Stream: Is there code for X?

Topic: sorting in monad


Marcus Rossel (Sep 27 2024 at 16:51):

Is there a way to sort a List or Array with a comparison function of the form α → α → m Bool for some monad m?

Adam Topaz (Sep 27 2024 at 17:01):

It's easy enough to implement a quicksort:

def List.qsortM [Monad m] (comp : α  α  m Bool) : List α  m (List α )
  | [] => return []
  | x :: xs => do
    let fst  (xs.filterM fun t => comp t x)
    let lst  (xs.filterM fun t => comp x t)
    return fst ++ [x] ++ lst

Adam Topaz (Sep 27 2024 at 17:02):

(this isn't correct if the list has repetitions)

Eric Wieser (Sep 27 2024 at 17:21):

That's surely just not correct at all as it doesn't recurse?

Adam Topaz (Sep 27 2024 at 17:21):

oops!

Adam Topaz (Sep 27 2024 at 17:22):

partial
def List.qsortM [Monad m] (comp : α  α  m Bool) [BEq α] : List α  m (List α )
  | [] => return []
  | x :: xs => do
    let fst  (xs.filterM fun t => comp t x)
    let lst  (xs.filterM fun t => return ( comp x t) || x == t)
    return ( fst.qsortM comp) ++ [x] ++ ( lst.qsortM comp)

Adam Topaz (Sep 27 2024 at 17:23):

okay, I think that last version might actually be correct

Adam Topaz (Sep 27 2024 at 17:25):

At least the following single test comes up correctly:

#eval List.qsortM (m := IO) (fun a b : Nat => do IO.sleep 1 ; return a < b) [1,45,2,7,1,2]

Eric Wieser (Sep 27 2024 at 17:31):

I think you probably want a List.partitionM out of that

Adam Topaz (Sep 27 2024 at 17:32):

I didn't know that exists.

Adam Topaz (Sep 27 2024 at 17:33):

That's indeed nicer:

partial
def List.qsortM [Monad m] (comp : α  α  m Bool) [BEq α] : List α  m (List α )
  | [] => return []
  | x :: xs => do
    let (fst, lst)  xs.partitionM fun t => comp t x
    return ( fst.qsortM comp) ++ [x] ++ ( lst.qsortM comp)

#eval List.qsortM (m := IO) (fun a b : Nat => do IO.sleep 1 ; return a < b) [1,45,2,7,1,2]

Eric Wieser (Sep 27 2024 at 17:36):

Does having something like

@[inline] def partitionM' [Monad m] (p : α  m Bool) (as : List α) :
    m {tf : List α × List α // tf.1 ++ tf.2 ~ as} :=
  return ( loop as ([], [])).map id fun x => by simp
where
  @[specialize] loop : (as : List α)  (acc : List α × List α) 
      m ({tf : List α × List α // tf.1 ++ tf.2 ~ as ++ acc.1 ++ acc.2})
  | [],    (bs, cs) => return (bs.reverse, cs.reverse), .append (by simp) (by simp)
  | a::as, (bs, cs) => do
    match  p a with
    | true  => pure <| ( loop as (a::bs, cs)).map id (fun x h => h.trans <| by aesop)
    | false => pure <| ( loop as (bs, a::cs)).map id (fun x h => h.trans <| by
      dsimp
      sorry)

help show termination?

Eric Wieser (Sep 27 2024 at 17:37):

You can't prove that the partition is lawful, because p might be "flip a coin"; but you could prove it doesn't change the length of the list, which might be enough to terminate

Eric Wieser (Sep 27 2024 at 17:41):

Yeah, I think that works:

def List.qsortM {α m} [Monad m] (comp : α  α  m Bool) : (l : List α)  m {l' : List α // l' ~ l}
  | [] => return [], .refl _⟩
  | x :: xs => do
    let (fst, lst), hp  (xs.partitionM' fun t => comp t x)
    have : fst.length  xs.length := sorry
    have : lst.length  xs.length := sorry
    let fst  fst.qsortM comp
    let lst  lst.qsortM comp
    return (fst) ++ [x] ++ (lst), sorry
termination_by xs => xs.length

Last updated: May 02 2025 at 03:31 UTC