Package: src/packages/lists.fdoc

key file
list.flx share/lib/std/datatype/list.flx
assoc_list.flx share/lib/std/datatype/assoc_list.flx
ralist.flx share/lib/std/datatype/ralist.flx
sexpr.flx share/lib/std/datatype/sexpr.flx
lsexpr.flx share/lib/std/datatype/lsexpr.flx
dlist.flx share/lib/std/datatype/dlist.flx

Functional List

The list type.

The core data type for most functional programming languages.

//[list.flx]
open class List
{
  variant list[T] = | Empty | Snoc of list[T] * T;
  fun _match_ctor_Cons[T] : list[T] -> bool = "!!$1";
  inline fun _ctor_arg_Cons[T]: list[T] -> T * list[T] =
    "reinterpret<#0>(flx::list::snoc2cons<?1>($1))"
    requires snoc2cons_h
  ;
  inline fun Cons[T] (h:T, t:list[T]) => Snoc (t,h);

  header snoc2cons_h = """
    namespace flx { namespace list {
      template<class T> struct snoc { void *mem_0; T mem_1; };
      template<class T> struct cons { T mem_0; void * mem_1; };
      template<class T> cons<T> snoc2cons (void *x) {
        return cons<T> {((snoc<T>*)x)->mem_1, ((snoc<T>*)x)->mem_0};
      }
    }}
  """;

Splice

This is primarily a non-functional helper routine.

//[list.flx]
  //$ The second list is made the tail of the
  //$ list stored at the location pointed at by the first argument.
  //$ If the first list is empty, the variable will point
  //$ at the second list. This operation is DANGEROUS because
  //$ it is a mutator: lists are traditionally purely functional.

  // NOTE: this will fail if the second argument is named "p"!
  // fix as for rev, rev_last!
  proc splice[T] : &list[T] * list[T] =
    """
    { // list splice
      //struct node_t { ?1 elt; void *tail; };
      struct node_t { void *tail; ?1 elt; };
      void **p = $1;
      while(*p) p = &((node_t*)FLX_VNP(*p))->tail;
      *p = $2;
    }
    """
  ;

In-place unsafe reversal.

Another helper routine.

//[list.flx]
  //$ In place list reversal: unsafe!
  // second arg is a dummy to make overload work
  proc rev[T,PLT=&list[T]] : &list[T] = "_rev($1,(?1*)0);" requires _iprev_[T,PLT];

  body _iprev_[T,PLT]=
    """
    static void _rev(?2 plt, ?1*) // second arg is a dummy
    { // in place reversal
      //struct node_t { ?1 elt; void *tail; };
      struct node_t { void *tail; ?1 elt; };
      void *nutail = 0;
      void *cur = *plt;
      while(cur)
      {
        void *oldtail = ((node_t*)FLX_VNP(cur))->tail;   // save old tail in temp
        ((node_t*)FLX_VNP(cur))->tail = nutail;          // overwrite current node tail
        nutail = cur;                                   // set new tail to current
        cur = oldtail;                                  // set current to saved old tail
      }
      *plt = nutail;                                    // overwrite
    }
    """
  ;

In-place reversal.

Another variant of the unsafe reversal.

//[list.flx]
  // in place list reversal, also returns the last element
  // as a list, empty iff the original list is
  // unsafe!
  proc rev_last[T,PLT=&list[T]] : &list[T] * &list[T] = "_rev_last($1,$2,(?1*)0);" requires _rev_last_[T,PLT];

  body _rev_last_[T,PLT]=
    """
    static void _rev_last(?2 p1, ?2 p2, ?1*)
    { // in place reversal returns tail as well
      //struct node_t { ?1 elt; void *tail; };
      struct node_t { void *tail; ?1 elt; };
      void *nutail = (void*)0;                 // new temp tail
      void *cur = *p1;                         // list to reverse
      void *last = cur;                        // save head
      while(cur)
      {
        void *oldtail = ((node_t*)FLX_VNP(cur))->tail;            // set old tail to current's tail
        ((node_t*)FLX_VNP(cur))->tail = nutail;                   // set current's tail to nutail
        nutail = cur;                                            // set nutail to current
        cur = oldtail;                                           // set current to old tail
      }
      *p1 = nutail;                                              // reversed list
      *p2 = last;                                                // original lists tail
    }
    """
  ;

List copy

Make an entirely new copy of a list. Primarily a helper.

//[list.flx]
  //$ Copy a list.
  fun copy[T] (x:list[T]):list[T]= {
    var y = rev x;
    rev (&y);
    return y;
  }

Copy and return last copy_last

Yet another helper.

//[list.flx]
  //$ Copy a list, and return last element as a list,
  //$ empty if original list was empty.
  proc copy_last[T] (inp:list[T], out:&list[T], last:&list[T]) {
    out <- rev inp;
    rev_last (out, last);
  }

Constructors

Named constructor for empty list.

//[list.flx]
  //$ Make an empty list.
  ctor[T] list[T] () => Empty[T];

Construct a singleton list.

Does not work if the argument is an array or option iterator.

//[list.flx]
  //$ Make a list with one element.
  //$ NOTE: list (1,2) is a list of 2 ints.
  //$ To get a list of one pair use list[int*int] (1,2) instead!
  ctor[T] list[T] (x:T) => Snoc(Empty[T],x);

Construct a list from an array.

//[list.flx]
  //$ Make a list from an array.
  ctor[T,N] list[T] (x:array[T, N]) = {
    var o = Empty[T];
    if x.len > 0uz do
      for var i in x.len.int - 1 downto 0 do
        o = Snoc(o,x.i);
      done
    done
    return o;
  }

List comprehension.

Make a list from an option stream. Named variant.

//[list.flx]
  //$ List comprehension:
  //$ Make a list from a stream.
  fun list_comprehension[T] (f: (1->opt[T])) = {
    var ff = f;
    fun aux (l:list[T]) = {
      var x = ff();
      return
        match x with
       | Some elt => aux (Snoc(l,elt))
       | #None => rev l
       endmatch
      ;
    }
    return aux Empty[T];
  }

List comprehension.

Make a list from an option stream. Constructor variant.

//[list.flx]
//$ List comprehension:
  //$ Make a list from a stream.
  ctor[T] list[T](f: (1->opt[T])) => list_comprehension f;

Construe a list as an array value.

//[list.flx]
  //$ Contrue a list as an array value
  instance[T] ArrayValue[list[T],T] {
//[list.flx]
    //$ Return umber of elements in a list.
    pure fun len (x:list[T]) = {
      fun aux (acc:size) (x:list[T]) =>
        match x with
        | #Empty => acc
        | Snoc(t,_) => aux (acc + 1uz) t
        endmatch
      ;
      return aux 0uz x;
    }
//[list.flx]
    //$ get n'th element
    pure fun unsafe_get: list[T] * size -> T =
      | Snoc(_,h), 0uz => h
      | Snoc(t,_), i => unsafe_get (t, i - 1uz)
    ;
//[list.flx]
    //$ Apply a procedure to each element of a list.
    proc iter (_f:T->void) (x:list[T]) {
      match x with
      | #Empty => {}
      | Snoc(t,h) => { _f h; iter _f t; }
      endmatch
      ;
    }
//[list.flx]
    //$ Traditional left fold over list (tail rec).
    fun fold_left[U] (_f:U->T->U) (init:U) (x:list[T]):U =
    {
      fun aux (init:U) (x:list[T]):U =>
        match x with
        | #Empty => init
        | Snoc(t,h) => aux (_f init h) t
        endmatch
      ;
      return aux init x;
    }
//[list.flx]
    //$ Right fold over list (not tail rec!).
    fun fold_right[U] (_f:T->U->U) (x:list[T]) (init:U):U =
    {
      fun aux (x:list[T]) (init:U):U =>
        match x with
        | #Empty => init
        | Snoc(t,h) => _f h (aux t init)
        endmatch
      ;
      return aux x init;
    }

  }

Destructors

Test for empty list is_empty

//[list.flx]
  //$ Test if a list is empty.
  pure fun is_empty[T] : list[T] -> 2 =
    | #Empty => true
    | _ => false
  ;

Tail of a list tail

//[list.flx]
  //$ Tail of a list, abort with match failure if list is empty.
  pure fun tail[T] (x:list[T]) : list[T] = {
    match x with
    | Snoc(t,_) => return t;
    endmatch;
  }

Head of a list head

//[list.flx]
  //$ Head of a list, abort with match failure if list is empty.
  pure fun head[T] (x:list[T]) : T = {
    match x with
    | Snoc(_,h) => return h;
    endmatch;
  }

Maps

Reverse map a list rev_map

Tail recursive.

//[list.flx]
  //$ map a list, return mapped list in reverse order (tail rec).
  fun rev_map[T,U] (_f:T->U) (x:list[T]): list[U] = {
    fun aux (inp:list[T]) (out:list[U]) : list[U] =>
      match inp with
      | #Empty => out
      | Snoc(t,h) => aux t (Snoc(out,_f(h)))
      endmatch
    ;
    return aux x Empty[U];
  }

Map a list map

Tail recursive. Uses rev_map and then inplace revseral. This is safe because we enforce linearity by abstraction.

//[list.flx]
  //$ map a list (tail-rec).
  //  tail rec due to in-place reversal of result.
  fun map[T,U] (_f:T->U) (x:list[T]): list[U] =
  {
    var r = rev_map _f x;
    rev$ &r;
    return r;
  }

Reverse a list rev.

Tail recursive.

//[list.flx]
  //$ reverse a list (tail rec).
  pure fun rev[T] (x:list[T]):list[T]= {
    fun aux (x:list[T]) (y:list[T]) : list[T] =
    {
      return
        match x with
        | #Empty => y
        | Snoc(t,h) => aux t (Snoc(y,h))
        endmatch
      ;
    }
    return aux x Empty[T];
  }

  fun urev[T](x:list[T]) => box (rev x);
  fun urev[T](var x:uniq (list[T])) : uniq (list[T]) {
    var y = unbox x;
    rev &y;
    return box y;
  }

Zip a pair of lists to a list of pairs zip2

Returns a list the length of the shortest argument.

//[list.flx]
  //$ Zip two lists into a list of pairs.
  //$ Zips to length of shortest list.
  fun zip2[T1,T2] (l1: list[T1]) (l2: list[T2]) : list[T1 * T2] =
  {
    fun aux (l1: list[T1]) (l2: list[T2]) (acc: list[T1 * T2]) =>
      match l1, l2 with
      | Snoc(t1,h1), Snoc(t2,h2) => aux t1 t2 (Snoc (acc, (h1, h2)))
      | _ => rev acc
      endmatch
    ;
    return aux l1 l2 Empty[T1 * T2];
  }

Useful lists

A list of integers range.

From low to high exclusive with given step.

//[list.flx]
  //$ Generate an ordered list of ints between low and high with given step.
  //$ Low included, high not included.
  fun range (low:int, high:int, step:int) =
  {
    fun inner(low:int, high:int, step:int, values:list[int]) =
    {
      return
        if high < low
          then values
          else inner(low, high - step, step, Snoc(values,high))
          endif
      ;
    }

    // reverse low and high so we can do negative steps
    lo, hi, s := if low < high
      then low, high, step
      else high, low, -step
      endif;

    // adjust the high to be the actual last value so we don't
    // have to reverse the list
    n := hi - lo - 1;

    return if s <= 0
      then Empty[int]
      else inner(lo, lo + n - (n % s), s, Empty[int])
      endif
    ;
  }

Consecutive integers range

//[list.flx]
  //$ Range with step 1.
  fun range (low:int, high:int) => range(low, high, 1);

Non-negative integers to limit range

//[list.flx]
  //$ Range from 0 to num (excluded).
  fun range (num:int) => range(0, num, 1);

Operators

Concatenate two lists join.

//[list.flx]
  //$ Concatenate two lists.
  fun join[T] (x:list[T]) (y:list[T]):list[T] =
  {
    if is_empty x do
      return y;
    else
      var z: list[T];
      var last: list[T];
      copy_last (x,&z,&last);
      splice (&last, y);
      return z;
    done;
  }

  //$ Concatenate two lists.
  pure fun + [T] (x:list[T], y: list[T]):list[T] => join x y;

  proc += [T] (x:&list[T], y: list[T]) => x <- join (*x) y;

Cons an element onto a list.

//[list.flx]
  //$ Prepend element to head of list.
  pure fun + [T] (x:T, y:list[T]):list[T] => Snoc(y,x);

Append an element onto a list.

O(N) slow.

//[list.flx]
  //$ Append element to tail of list (slow!).
  noinline fun + [T] (x:list[T], y:T):list[T] => rev$ Snoc (rev x,y);

  //$ Append element to tail of list (slow!).
  proc += [T] (x:&list[T], y:T) { x <- *x + y; }

  //$ Prepend element to head of list (fast!).
  proc -= [T] (x:&list[T], y:T) { x <- y ! *x; }

Outer product.

Given a list of lists of T named x and a list of lists of T named y, return a list of lists of T, consisting of every combination xelt + yelt where e in x, f in y.

Note: this is a special case of a second order fold.

//[list.flx]

noinline fun outer_product[T] (x:list[list[T]]) (y:list[list[T]]): list[list[T]] =
{
  var res = Empty[list[T]];

  for xelt in x
  for yelt in y
    perform res = (xelt + yelt) ! res;
  return res;
}

Concatenate a list of lists cat

//[list.flx]
  //$ Concatenate all the lists in a list of lists.
  noinline fun cat[T] (x:list[list[T]]):list[T] =
  {
     return
       match x with
       | #Empty => Empty[T]
       | Snoc(t,h) => fold_left join of (list[T]) h t
       endmatch
     ;
   }

Lists and Strings

Pack list of strings into a string with separator cat

//[list.flx]
  //$ Concatenate all the strings in a list with given separator.
  pure fun cat (sep:string) (x:list[string]):string =
  {
    var n = 0uz;
    for s in x perform n += s.len+1uz;
    var r = "";
    reserve (&r,n);
    match x with
    | #Empty => return r;
    | Snoc (tail, head) =>
      r = head;
      var tl = tail;
  next:>
      match tl with
      | #Empty => return r;
      | Snoc(t,h) =>
        r += sep + h;
        tl = t;
        goto next;
      endmatch;
    endmatch;
    return r;
  }

Map a list to a list of strings and cat with separator catmap

//[list.flx]
  fun catmap[T] (sep:string) (f:T -> string) (ls: list[T]) =>
    cat sep (map f ls)
  ;

  fun strcat[T with Str[T]]  (sep: string) (ls: list[T]) =>
    catmap sep (str of (T)) ls
  ;

  fun strcat[T with Str[T]]  (ls: list[T]) =>
    catmap ", " (str of (T)) ls
  ;

Searching

Value membership

//[list.flx]
  //$ Return true if one value in a list satisfies the predicate.
  fun mem[T] (eq:T -> bool) (xs:list[T]) : bool =>
    match xs with
    | #Empty => false
    | Snoc(t,h) => if eq(h) then true else mem eq t endif
    endmatch
  ;

  //$ Return true if one value in the list satisfies the relation
  //$ in the left slot with
  //$ the given element on the right slot.
  fun mem[T, U] (eq:T * U -> bool) (xs:list[T]) (e:U) : bool =>
    mem (fun (x:T) => eq(x, e)) xs
  ;

  //$ Construe a list as a set, imbuing it with a membership
  //$ test, provided the element type has an equality operator.
  instance[T with Eq[T]] Set[list[T],T] {
    fun \in (x:T, a:list[T]) => mem[T,T] eq of (T * T) a x;
  }

Value Find by relation find

Returns option.

//[list.flx]
  //$ return option of the first element in a list satisfying the predicate.
  fun find[T] (eq:T -> bool) (xs:list[T]) : opt[T] =>
    match xs with
    | #Empty => None[T]
    | Snoc(t,h) => if eq(h) then Some h else find eq t endif
    endmatch
  ;


  //$ Return option the first value in the list satisfies the relation
  //$ in the left slot with
  //$ the given element on the right slot.
  fun find[T, U] (eq:T * U -> bool) (xs:list[T]) (e:U) : opt[T] =>
    find (fun (x:T) => eq(x, e)) xs;
  ;

  //$ Return a sub list with elements satisfying the given predicate.
  noinline fun filter[T] (P:T -> bool) (x:list[T]) : list[T] =
  {
    fun aux (inp:list[T], out: list[T]) =>
      match inp with
      | #Empty => rev out
      | Snoc(t,h) =>
        if P(h) then aux(t,Snoc(out,h))
        else aux (t,out)
        endif
      endmatch
    ;
    return aux (x,Empty[T]);
  }

  //$ Push element onto front of list if there isn't one in the
  //$ list already satisfying the relation.
  fun prepend_unique[T] (eq: T * T -> bool) (x:list[T]) (e:T) : list[T] =>
    if mem eq x e then x else Snoc(x,e) endif
  ;

  //$ Attach element to tail of list if there isn't one in the
  //$ list already satisfying the relation.
  fun insert_unique[T] (eq: T * T -> bool) (x:list[T]) (e:T) : list[T] =>
    if mem eq x e then x else rev$ Snoc (rev x,e) endif
  ;

  //$ Remove all elements from a list satisfying relation.
  fun remove[T] (eq: T * T -> bool) (x:list[T]) (e:T) : list[T] =>
    filter (fun (y:T) => not eq (e,y)) x
  ;

  //$ Attach element to tail of list if there isn't one in the
  //$ list already satisfying the relation (tail-rec).
  noinline fun append_unique[T] (eq: T * T -> bool) (x:list[T]) (e:T) : list[T] = {
    fun aux (inp:list[T], out: list[T]) =>
      match inp with
      | #Empty => rev$ Snoc(out,e)
      | Snoc(t,h) =>
        if not eq (h, e) then aux(t,Snoc(out,h))
        else aux (t,out)
        endif
      endmatch
    ;
    return aux (x,Empty[T]);
  }

  //$ Take the first k elements from a list.
  fun take[T] (k:int) (lst:list[T]) : list[T] =>
    if k <= 0 then
      list[T] ()
    else
      match lst with
        | #Empty => list[T] ()
        | Snoc(xs,x) => join (list[T] x) (take[T] (k - 1) xs)
      endmatch
    endif
  ;

  //$ Drop the first k elements from a list.
  fun drop[T] (k:int) (lst:list[T]) : list[T] =>
    if k <= 0 then
      lst
    else
      match lst with
        | #Empty => list[T] ()
        | Snoc(xs,x) => drop (k - 1) xs
    endif
  ;

  fun scroll1[T] (left: list[T], right: list[T]) =>
    match left with
    | h ! t => t, h ! right
    | _ => left, right
  ;
  fun scroll[T] (lr:list[T] * list[T]) (n:int) =>
    if n <= 0 then lr else
    scroll (scroll1 lr) (n - 1)
  ;

  // return revhead, tail where revhead is first k elements
  // of lst, in reverse order, and tail is what is left over
  // cannot fail: if k is not big enough the tail just ends
  // up empty and the function is equivalent to rev.
  fun revsplit[T] (k:int) (lst:list[T]) : list[T] * list[T] =>
    let fun aux (k:int) (revhead:list[T]) (tail:list[T]) =>
      if k <=0 then revhead,tail
      else match tail with
      | #Empty => revhead, tail
      | h ! t => aux (k - 1) (h!revhead) t
      endmatch
    in aux k Empty[T] lst
  ;

  fun list_eq[T with Eq[T]] (a:list[T], b:list[T]): bool =>
    match a, b with
    | #Empty, #Empty => true
    | #Empty, _ => false
    | _,#Empty => false
    | Snoc(ta,ha), Snoc(tb,hb) =>
      if not (ha == hb) then false
      else list_eq (ta, tb)
      endif
    endmatch
  ;
  instance[T with Eq[T]] Eq[list[T]] {
    fun ==(a:list[T], b:list[T])=> list_eq(a,b);
  }

Sort

//[list.flx]
  //$ Sort a list with given less than operator, which must be
  //$ total order. Uses varray sort (which uses STL sort).
  fun sort[T] (lt:T*T->bool) (x:list[T])=
  {
    val n = len x;
    var a = varray[T]$ n;
    iter (proc (e:T) { a+=e; }) x;
    sort lt a;
    var r = Empty[T];
    if n > 0uz do
      for var i in n - 1uz downto 0uz do r = Snoc(r,a.i); done
    done
    return r;
  }

  //$ Sort a list with default total order.
  //$ Uses varray sort (which uses STL sort).
  fun sort[T with Tord[T]](x:list[T])=> sort lt x;

Streaming list

//[list.flx]
  instance[T] Iterable[list[T],T] {
  //$ Convert a list to a stream.
    gen iterator (var xs:list[T]) () = {
      while true do
        match xs with
        | Snoc(t,h) => xs = t; yield Some h;
        | #Empty => return None[T];
        endmatch;
      done
    }
  }
  inherit[T] Streamable[list[T],T];

  inherit [T with Str[T]] Str[list[T]];
  inherit [T with Eq[T]] Set[list[T],T];
  inherit[T] ArrayValue[list[T],T];

}

open [T with Eq[T]] Eq[List::list[T]];

//open [T with Str[T]] Str[list[T]];
//open [T with Eq[T]] Set[list[T],T];

// display list as string given element type with str operator
// elements are separated by a comma and one space
instance[T with Show[T]] Str[List::list[T]] {
  noinline fun str (xs:List::list[T]) =>
    'list(' +
      match xs with
      | #Empty => ''
      | Snoc(os,o) =>
          List::fold_left (
            fun (a:string) (b:T):string => a + ', ' + (repr b)
          ) (repr o) os
      endmatch
    + ')'
  ;
}

Association List

A list of pairs

//[assoc_list.flx]
open class Assoc_list
{
  typedef assoc_list[A,B] = List::list[A*B];

  // check is the key (left element) of a pair
  // satisfies the predicate
  fun mem[A,B] (eq:A -> bool) (xs:assoc_list[A,B]) : bool =>
    List::mem (fun (a:A, b:B) => eq a) xs;
  ;

  // check is the key (left element) of a pair
  // satisfies the relation to given element
  fun mem[A,B,T] (eq:A * T -> bool) (xs:assoc_list[A,B]) (e:T) : bool =>
    mem (fun (a:A) => eq(a, e)) xs;
  ;

  instance[A,B] Set[assoc_list[A,B], A] {
    fun mem[A,B with Eq[A]] (xs:assoc_list[A,B]) (e:A) : bool =>
      mem eq of (A * A) xs e
    ;
  }

  // find optionally the first value whose associate key satisfies
  // the given predicate
  fun find[A,B] (eq:A -> bool) (xs:assoc_list[A,B]) : opt[B] =>
    match xs with
    | #Empty => None[B]
    | Snoc (t,(a, b)) => if eq(a) then Some b else find eq t endif
    endmatch
  ;

  // find optionally the first value whose associate key (left slot)
  // satisfies the given relation to the given element (right slot)
  fun find[A,B,T] (eq:A * T -> bool) (xs:assoc_list[A,B]) (e:T) : opt[B] =>
    find (fun (a:A) => eq (a, e)) xs;
  ;

  fun find[A,B with Eq[A]] (xs:assoc_list[A,B]) (e:A) : opt[B] =>
    find eq of (A * A) xs e
  ;
}

Purely Functional Random Access List.

//[ralist.flx]
//$ Purely functional Random Access List.
//$ Based on design from Okasaki, Purely Functional Datastructures.
//$ Transcribed from Hongwei Xi's encoding for ATS2 library.
//$
//$ An ralist provides O(log N) indexed access and amortised
//$ O(1) consing. This is roughly the closest thing to
//$ purely functional array available.

class Ralist
{

  //$ Auxilliary data structure.
  variant pt[a] = | N1 of a | N2 of pt[a] * pt[a];

  //$ Type of an ralist.
  variant ralist[a] =
    | RAnil
    | RAevn of ralist[a]
    | RAodd of pt[a] * ralist[a]
  ;

  //$ Length of an ralist.
  fun ralist_length[a] : ralist[a] -> int =
    | #RAnil => 0
    | RAevn xxs => 2 * ralist_length xxs
    | RAodd (_,xxs) => 2 * ralist_length xxs + 1
  ;

  private fun cons[a] // O(1), amortized
    (x0: pt[a], xs: ralist[a]): ralist [a] =>
    match xs with
    | #RAnil => RAodd (x0, RAnil[a])
    | RAevn xxs => RAodd (x0, xxs)
    | RAodd (x1, xxs) =>
        let x0x1 = N2 (x0, x1) in
        RAevn (cons (x0x1, xxs) )
    endmatch  ;

  //$ Cons: new list with extra value at the head.
  fun ralist_cons[a] (x:a, xs: ralist[a]) =>
    cons (N1 x, xs)
  ;

  //$ Check for an empty list.
  fun ralist_empty[a]: ralist[a] -> bool  =
  | #RAnil => true
  | _ => false
  ;

  private proc uncons[a] (xs: ralist[a], phd: &pt[a], ptl: &ralist[a])
  {
    match xs with
    | RAevn xss =>
      var nxx: pt[a];
      var xxs: ralist[a];
      uncons (xss,&nxx, &xxs);
      match nxx with
      | N2(x0,x1) =>
        phd <- x0;
        ptl <- RAodd (x1,xxs);
      endmatch;

    | RAodd (x0,xss) =>
      phd <- x0;
      match xss with
      | #RAnil => ptl <- RAnil[a];
      | _ => ptl <- RAevn xss;
      endmatch;
    endmatch;
  }

  //$ Proedure to split a non-empty ralist
  //$ into a head element and a tail.
  proc ralist_uncons[a] (xs: ralist[a], phd: &a, ptl: &ralist[a])
  {
    var nx: pt[a];
    uncons (xs, &nx, ptl);
    match nx with
    | N1 (x1) => phd <- x1;
    endmatch;
  }

  //$ User define pattern matching support
  fun _match_ctor_Cons[T] (x:ralist[T]) =>not ( ralist_empty x);
  fun _match_ctor_Empty[T] (x:ralist[T]) => ralist_empty x;

  fun _ctor_arg_Cons[T] (x:ralist[T]) : T * ralist[T] =
  {
    var elt : T;
    var tail : ralist[T];
    ralist_uncons (x, &elt, &tail);
    return elt,tail;
  }


  //$ Head element of a non-empty ralist.
  fun ralist_head[a] (xs: ralist[a]) : a =
  {
    var nx: a;
    var xxs: ralist[a];
    ralist_uncons (xs, &nx, &xxs);
    return nx;
  }

  //$ Tail list of a non-empty ralist.
  fun ralist_tail[a] (xs: ralist[a]) : ralist[a] =
  {
    var nx: a;
    var xxs: ralist[a];
    ralist_uncons (xs, &nx, &xxs);
    return xxs;
  }

  private fun lookup[a]
  (
    xs: ralist [a],
    i: int
  ) : pt[a] =>
    match xs with
    | RAevn xxs =>
      let x01 = lookup (xxs, i/2) in
      if i % 2 == 0 then
        let N2 (x0, _) = x01 in x0
      else
        let N2 (_, x1) = x01 in x1
      endif

    | RAodd (x, xxs) =>
      if i == 0 then x else
        let x01 = lookup (xxs, (i - 1)/2) in
        if i % 2 == 0 then
          let N2 (_, x1) = x01 in x1
        else
          let N2 (x0, _) = x01 in x0
        endif
      endif
    endmatch
  ;

  //$ Random access to an ralist. Unchecked.
  fun ralist_lookup[a] (xs:ralist[a],i:int)=>
    let N1 x = lookup (xs,i) in x
  ;

  private fun fupdate[a]
  (
    xs: ralist[a] ,
    i:int,
    f: pt[a] -> pt[a]
  ) : ralist[a] =>
    match xs with
    | RAevn (xxs) => RAevn (fupdate2 (xxs, i, f))
    | RAodd (x, xxs) =>
      if i == 0 then RAodd (f x, xxs)
      else RAodd (x, fupdate2 (xxs, i - 1, f))
      endif
    endmatch
  ;

  private fun fupdate2[a]
  (
    xxs: ralist[a],
    i: int,
    f: pt[a] -> pt[a]
  ) : ralist[a] =>
      if i % 2 == 0 then
      let f1 =
        fun (xx: pt[a]): pt[a] =>
        let N2 (x0, x1) = xx in N2 (f x0, x1)
      in
      fupdate (xxs, i / 2, f1)
    else
      let f1 =
        fun (xx: pt[a]): pt[a] =>
        let N2 (x0, x1) = xx in N2 (x0, f x1)
      in
      fupdate (xxs, i / 2, f1)
  ;

  //$ Return a list with the i'th element replaced by x0.
  //$ Index is unchecked.
  fun ralist_update[a] (xs:ralist[a], i:int, x0:a) =>
    let f = fun (z:pt[a]) : pt[a] => N1 x0 in
    fupdate (xs,i,f)
  ;

  private proc foreach[a]
  (
    xs: ralist[a],
    f: pt[a] -> void
  )
  {
    match xs with
    | RAevn (xxs) => foreach2 (xxs, f);
    | RAodd (x, xxs) =>
      f x;
      match xxs with
      | #RAnil => ;
      | _ => foreach2 (xxs, f);
      endmatch;
    | #RAnil => ;
    endmatch;
  }

  private proc foreach2[a]
  (
    xxs: ralist[a],
    f: pt[a] -> void
  )
  {
    var f1 =
      proc (xx: pt[a]) {
        match xx with
        | N2 (x0, x1) => f (x0); f (x1);
        endmatch;
      }
    ;
    foreach (xxs, f1);
  }

  //$ Callback based iteration.
  //$ Apply procedure to each element of the ralist.
  proc ralist_foreach[a]
  (
    xs: ralist[a],
    f: a -> void
  )
  {
    var f2 =
      proc (x:pt[a]) {
        match x with
        | N1 y => f y;
        endmatch;
      }
    ;
    foreach (xs, f2);
  }

  //$ Convert ralist to a string.
  instance[a with Str[a]] Str[ralist[a]]
  {
    fun str (xx: ralist[a]):string = {
      var xs = xx;
      var x: a;
      var s = "";
      while not ralist_empty xs do
        ralist_uncons (xs,&x,&xs);
        s += (if s != "" then "," else "") + str x;
      done
      return s;
    }
  }

  // TODO: list membership, folds, etc
}

Dlist

A dlist_t is a doubly linked mutable list. It is suitable for use as non-thread-safe queue.

//[dlist.flx]
class DList[T]
{
  typedef dnode_t=
  (
    data: T,
    next: cptr[dnode_t], // possibly NULL
    prev: cptr[dnode_t]  // possibly NULL
  );
  typedef dlist_t = (first:cptr[dnode_t], last:cptr[dnode_t]);
    // invariant: if first is null, so is last!

  ctor dlist_t () => (first=nullptr[dnode_t],last=nullptr[dnode_t]);

Length len

//[dlist.flx]
  fun len (x:dlist_t) = {
    var n = 0;
    var first : cptr[dnode_t] = x.first;
  again:>
    match first do
    | #nullptr => return n;
    | Ptr p => ++n; first = p*.next;
    done
    goto again;
  }

Inspection

//[dlist.flx]
  fun peek_front (dl:dlist_t) : opt[T] =>
    match dl.first with
    | #nullptr => None[T]
    | Ptr p => Some p*.data
    endmatch
  ;

  fun peek_back (dl:dlist_t) : opt[T] =>
    match dl.last with
    | #nullptr => None[T]
    | Ptr p => Some p*.data
    endmatch
  ;

Insertion

//[dlist.flx]
  proc push_front (dl:&dlist_t, v:T) {
    var oldfirst = dl*.first;
    var node = new (data=v, next=oldfirst, prev=nullptr[dnode_t]);
    dl.first <- Ptr node;
    match oldfirst with
    | #nullptr => dl.last
    | Ptr p => p.prev
    endmatch <- Ptr node;
  }

  proc push_back (dl:&dlist_t, v:T) {
    var oldlast = dl*.last;
    var node = new (data=v, next=nullptr[dnode_t], prev=oldlast);
    dl.last <- Ptr node;
    match oldlast with
    | #nullptr => dl.first
    | Ptr p => p.next
    endmatch <- Ptr node;
  }

Deletion

//[dlist.flx]

  gen pop_front (dl:&dlist_t): opt[T] = {
    match dl*.first do
    | #nullptr => return None[T];
    | Ptr p =>
      match p*.next do
      | #nullptr =>
        dl.first <- nullptr[dnode_t];
        dl.last <- nullptr[dnode_t];
      | _ =>
        dl.first <- p*.next;
      done
      return Some p*.data;
    done
  }

  gen pop_back (dl:&dlist_t): opt[T] = {
    match dl*.last do
    | #nullptr => return None[T];
    | Ptr p =>
      match p*.prev do
      | #nullptr =>
        dl.first <- nullptr[dnode_t];
        dl.last <- nullptr[dnode_t];
      | _ =>
        dl.last <- p*.prev;
      done
      return Some p*.data;
    done
  }

Use as a queue

We can implement enqueue and dequeue at either end, we’ll make enqueue push_front and dequeue pop_back for no particular reason.

//[dlist.flx]
  typedef queue_t = dlist_t;
  proc enqueue (q:&queue_t) (v:T) => push_front (q,v);
  gen dequeue (q:&queue_t) :opt[T] => pop_back q;
  ctor queue_t () => dlist_t ();

Queue iterator

Fetch everything from a queue.

//[dlist.flx]
  gen iterator (q:&queue_t) () => dequeue q;
}

S-expressions

A scheme like data structure.

//[sexpr.flx]
class S_expr
{
  variant sexpr[T] = Leaf of T | Tree of list[sexpr[T]];

  fun fold_left[T,U] (_f:U->T->U) (init:U) (x:sexpr[T]):U =>
    match x with
    | Leaf a => _f init a
    | Tree b => List::fold_left (S_expr::fold_left _f) init b
  ;

  proc iter[T] (_f:T->void) (x:sexpr[T]) {
    match x with
    | Leaf a => _f a;
    | Tree b => List::iter (S_expr::iter _f) b;
    endmatch;
  }

  fun map[T,U] (_f:T->U) (x:sexpr[T]):sexpr[U] =>
    match x with
    | Leaf a => Leaf (_f a)
    | Tree b => Tree ( List::map (S_expr::map _f) b )
  ;

  instance[T with Eq[T]] Set[sexpr[T],T] {
    fun \in (elt:T, x:sexpr[T]) =>
      fold_left (fun (acc:bool) (v:T) => acc or v == elt) false x;
  }
  instance[T with Str[T]] Str[sexpr[T]] {
    noinline fun str(x:sexpr[T])=>
      match x with
      | Leaf a => str a
      | Tree b => str b
    ;
  }

}

open[T with Str[T]] Str[S_expr::sexpr[T]];
open[T with Eq[T]] Set[S_expr::sexpr[T],T];

LS-expressions

A scheme like data structure, similar to sexpr, only in this variant the tree nodes also have labels.

//[lsexpr.flx]
class LS_expr
{
  variant lsexpr[T,L] = | Leaf of T | Tree of L * list[lsexpr[T,L]];

  fun fold_left[T,L,U] (_f:U->T->U) (_g:U->L->U) (init:U) (x:lsexpr[T,L]):U =>
    match x with
    | Leaf a => _f init a
    | Tree (a,b) => List::fold_left (LS_expr::fold_left _f _g) (_g init a) b
  ;

  proc iter[T,L] (_f:T->void) (_g:L->void) (x:lsexpr[T,L]) {
    match x with
    | Leaf a => _f a;
    | Tree (a,b) => _g a; List::iter (LS_expr::iter _f _g) b;
    endmatch;
  }

  fun map[T,L,U,V] (_f:T->U) (_g:L->V) (x:lsexpr[T,L]):lsexpr[U,V] =>
    match x with
    | Leaf a => Leaf[U,V] (_f a)
    | Tree (a,b) => Tree ( _g a, List::map (LS_expr::map _f _g) b )
  ;

  instance[T,L with Str[T], Str[L]] Str[lsexpr[T,L]] {
    noinline fun str(x:lsexpr[T,L])=>
      match x with
      | Leaf a => str a
      | Tree (a,b) => str a + "(" + str b  + ")"
    ;
  }

}

open[T,L with Str[T], Str[L]] Str[LS_expr::lsexpr[T,L]];