muro-lang.dev / docs

Indexed data

In brief: constructor targets are compared with conversion. suc is inverted, so vcons against Vec A (suc m) forces the length. Motives bind the indices, then the scrutinee. If the types line up, lookup has no runtime bounds check.

Fin and Vec

From examples/vec.muro:

data Fin : Nat  Type where
  fzero : Π (n : Nat)  Fin suc(n)
  fsuc  : Π (n : Nat)  Fin n  Fin suc(n)

data Vec (A : Type) : Nat  Type where
  vnil  : Vec A 0
  vcons : Π (n : Nat)  A  Vec A n  Vec A suc(n)

Fin n is a position smaller than n. Vec A n is a vector of length n. There are no metavariables: you write the length arguments.

Motives with indices

The motive is a telescope: first the indices, then the scrutinee.

match i motive (λ (k : Nat)  λ (_ : Fin k)  Vec A k  A)
  | fzero m => 
  | fsuc m j => 

lookup returns a function Vec A k → A, then applies it to xs. That is how the index k stays aligned with the vector you eliminate.

def lookup : run Π (-A : Type)  Π (n : Nat)  Π (i : Fin n)  Π (xs : Vec A n)  A :=
  λ (-A : Type)  λ (n : Nat)  λ (i : Fin n)  λ (xs : Vec A n) 
    (match i motive (λ (k : Nat)  λ (_ : Fin k)  Vec A k  A)
      | fzero m =>
          λ (ys : Vec A suc(m)) 
            (match ys motive (λ (k : Nat)  λ (_ : Vec A k)  A)
              | vnil => 0
              | vcons p a as => a)
      | fsuc m j =>
          λ (ys : Vec A suc(m)) 
            (match ys motive (λ (k : Nat)  λ (_ : Vec A k)  A)
              | vnil => 0
              | vcons p a as => lookup A p j as)) xs

The vnil branches are well-typed empty cases: conversion has already forced suc(m) against 0 to be impossible in a consistent book, but the surface still asks you to write a branch. The body 0 is a placeholder the checker accepts in that impossible corner of the motive; a real lookup never takes it when Fin n and Vec A n agree.

ones1 is a vector of length one. lookup-ok is refl:

def ones1 : run Vec Nat suc(0) :=
  vcons 0 (suc 0) vnil

def lookup-ok : evidence {lookup Nat suc(0) (fzero 0) ones1  suc 0 : Nat} :=
  refl

Running it

{:ok, src} = Muro.emit_file("examples/vec.muro", Muro.Vecs)
Code.eval_string(src)
Muro.Vecs.lookup({:suc, 0}, {:fzero, 0}, Muro.Vecs.ones1())
# {:suc, 0}

A was erased, so the Elixir arity starts at n. n is Peano {:suc, 0}. There is no bounds check in the emitted function: the index and the vector were already the same length in the type.

Next: Streams.

Source on GitHub · fetched from murolang/muro manual/