MyNixOS website logo
Description

Queues with verified and unverified versions.

This library provides implementations of five different queues (binomial, pairing, skew, leftist, and Braun), each in two flavours: one verified, and one not.

At the moment, only structural invariants are maintained.

More information, and a walkthough of a couple implementations, can be found at this blog post.

Comparisons of verified and unverified queues

Both versions of each queue are provided for comparison: for instance, compare the standard leftist queue (in Data.Queue.Leftist):

data Leftist a
  = Leaf
  | Node !Int
        a
        (Leftist a)
        (Leftist a)

To its size-indexed counterpart (in Data.Queue.Indexed.Leftist):

data Leftist n a where
        Leaf :: Leftist 0 a
        Node :: !(The Nat (n + m + 1))
             -> a
             -> Leftist n a
             -> Leftist m a
             -> !(m <= n)
             -> Leftist (n + m + 1) a

The invariant here (that the size of the left queue must always be less than that of the right) is encoded in the proof m <= n.

With that in mind, compare the unverified and verified implementatons of merge:

merge Leaf h2 = h2
merge h1 Leaf = h1
merge h1@(Node w1 p1 l1 r1) h2@(Node w2 p2 l2 r2)
  | p1 < p2 =
      if ll <= lr
          then Node (w1 + w2) p1 l1 (merge r1 h2)
          else Node (w1 + w2) p1 (merge r1 h2) l1
  | otherwise =
      if rl <= rr
          then Node (w1 + w2) p2 l2 (merge r2 h1)
          else Node (w1 + w2) p2 (merge r2 h1) l2
  where
    ll = rank r1 + w2
    lr = rank l1
    rl = rank r2 + w1
    rr = rank l2

Verified:

merge Leaf h2 = h2
merge h1 Leaf = h1
merge h1@(Node w1 p1 l1 r1 _) h2@(Node w2 p2 l2 r2 _)
  | p1 < p2 =
      if ll <=. lr
        then Node (w1 +. w2) p1 l1 (merge r1 h2)
        else Node (w1 +. w2) p1 (merge r1 h2) l1 . totalOrder ll lr
  | otherwise =
      if rl <=. rr
          then Node (w1 +. w2) p2 l2 (merge r2 h1)
          else Node (w1 +. w2) p2 (merge r2 h1) l2 . totalOrder rl rr
  where
    ll = rank r1 +. w2
    lr = rank l1
    rl = rank r2 +. w1
    rr = rank l2

Using type families and typechecker plugins to encode the invariant

The similarity is accomplished through overloading, and some handy functions. For instance, the second if-then-else works on boolean singletons, and the <=. function provides a proof of order along with its answer. The actual arithmetic is carried out at runtime on normal integers, rather than Peano numerals. These tricks are explained in more detail TypeLevel.Singletons and TypeLevel.Bool.

A typechecker plugin does most of the heavy lifting, although there are some (small) manual proofs.

Uses of verified queues

The main interesting use of these sturctures is total traversable sorting (sort-traversable). An implementation of this is provided in Data.Traversable.Parts. I'm interested in finding out other uses for these kinds of structures.

type-indexed-queues

Queues and Heaps with verified and unverified versions.

Hackage Build Status

This library provides implementations of five different heaps (binomial, pairing, skew, leftist, and Braun), each in two flavours: one verified, and one not.

At the moment, only structural invariants are maintained.

More information, and a walkthrough of a couple implementations, can be found at this blog post.

Comparisons of verified and unverified heaps

Both versions of each heap are provided for comparison: for instance, compare the standard leftist heap (in Data.Heap.Leftist):

data Leftist a
  = Leaf
  | Node !Int
        a
        (Leftist a)
        (Leftist a)

To its size-indexed counterpart (in Data.Heap.Indexed.Leftist):

data Leftist n a where
        Leaf :: Leftist 0 a
        Node :: !(The Nat (n + m + 1))
             -> a
             -> Leftist n a
             -> Leftist m a
             -> !(m <= n)
             -> Leftist (n + m + 1) a

The invariant here (that the size of the left heap must always be less than that of the right) is encoded in the proof m <= n.

With that in mind, compare the unverified and verified implementatons of merge:

merge Leaf h2 = h2
merge h1 Leaf = h1
merge h1@(Node w1 p1 l1 r1) h2@(Node w2 p2 l2 r2)
  | p1 < p2 =
      if ll <= lr
          then Node (w1 + w2) p1 l1 (merge r1 h2)
          else Node (w1 + w2) p1 (merge r1 h2) l1
  | otherwise =
      if rl <= rr
          then Node (w1 + w2) p2 l2 (merge r2 h1)
          else Node (w1 + w2) p2 (merge r2 h1) l2
  where
    ll = rank r1 + w2
    lr = rank l1
    rl = rank r2 + w1
    rr = rank l2

Verified:

merge Leaf h2 = h2
merge h1 Leaf = h1
merge h1@(Node w1 p1 l1 r1 _) h2@(Node w2 p2 l2 r2 _)
  | p1 < p2 =
      if ll <=. lr
        then Node (w1 +. w2) p1 l1 (merge r1 h2)
        else Node (w1 +. w2) p1 (merge r1 h2) l1 . totalOrder ll lr
  | otherwise =
      if rl <=. rr
          then Node (w1 +. w2) p2 l2 (merge r2 h1)
          else Node (w1 +. w2) p2 (merge r2 h1) l2 . totalOrder rl rr
  where
    ll = rank r1 +. w2
    lr = rank l1
    rl = rank r2 +. w1
    rr = rank l2

Using type families and typechecker plugins to encode the invariants

The similarity is accomplished through overloading, and some handy functions. For instance, the second if-then-else works on boolean singletons, and the <=. function provides a proof of order along with its answer. The actual arithmetic is carried out at runtime on normal integers, rather than Peano numerals. These tricks are explained in more detail TypeLevel.Singletons and TypeLevel.Bool.

A typechecker plugin does most of the heavy lifting, although there are some (small) manual proofs.

Uses of verified heaps

The main interesting use of these sturctures is total traversable sorting (sort-traversable). An implementation of this is provided in Data.Traversable.Parts. I'm interested in finding out other uses for these kinds of structures.

Metadata

Version

0.2.0.0

License

Platforms (77)

    Darwin
    FreeBSD
    Genode
    GHCJS
    Linux
    MMIXware
    NetBSD
    none
    OpenBSD
    Redox
    Solaris
    WASI
    Windows
Show all
  • aarch64-darwin
  • aarch64-freebsd
  • aarch64-genode
  • aarch64-linux
  • aarch64-netbsd
  • aarch64-none
  • aarch64-windows
  • aarch64_be-none
  • arm-none
  • armv5tel-linux
  • armv6l-linux
  • armv6l-netbsd
  • armv6l-none
  • armv7a-darwin
  • armv7a-linux
  • armv7a-netbsd
  • armv7l-linux
  • armv7l-netbsd
  • avr-none
  • i686-cygwin
  • i686-darwin
  • i686-freebsd
  • i686-genode
  • i686-linux
  • i686-netbsd
  • i686-none
  • i686-openbsd
  • i686-windows
  • javascript-ghcjs
  • loongarch64-linux
  • m68k-linux
  • m68k-netbsd
  • m68k-none
  • microblaze-linux
  • microblaze-none
  • microblazeel-linux
  • microblazeel-none
  • mips-linux
  • mips-none
  • mips64-linux
  • mips64-none
  • mips64el-linux
  • mipsel-linux
  • mipsel-netbsd
  • mmix-mmixware
  • msp430-none
  • or1k-none
  • powerpc-netbsd
  • powerpc-none
  • powerpc64-linux
  • powerpc64le-linux
  • powerpcle-none
  • riscv32-linux
  • riscv32-netbsd
  • riscv32-none
  • riscv64-linux
  • riscv64-netbsd
  • riscv64-none
  • rx-none
  • s390-linux
  • s390-none
  • s390x-linux
  • s390x-none
  • vc4-none
  • wasm32-wasi
  • wasm64-wasi
  • x86_64-cygwin
  • x86_64-darwin
  • x86_64-freebsd
  • x86_64-genode
  • x86_64-linux
  • x86_64-netbsd
  • x86_64-none
  • x86_64-openbsd
  • x86_64-redox
  • x86_64-solaris
  • x86_64-windows