diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,5 +1,31 @@
 # Revision history for zeolite-lang
 
+## 0.19.0.0  -- 2021-10-25
+
+### Compiler CLI
+
+* **[breaking]** Changes how `BoxedValue` is created for new `TypeValue`
+  instances in C++ extensions. Rather than `BoxedValue(new Foo(...))`, you must
+  now use `BoxedValue::New<Foo>(...)`. This breaks all existing extensions that
+  instantiate new values.
+
+* **[breaking]** Removes all passing of `Var_self` in C++ extensions. Use
+  `VAR_SELF` where `Var_self` was previously used, and delete the `Var_self`
+  argument from function signatures. This breaks all existing extensions.
+
+### Language
+
+* **[fix]** Fixes obscure memory leak in `weak` values, triggered by a very
+  specific sequence of events between three threads. This leak was unlikely to
+  affect any programs that did not specifically attempt to trigger it.
+
+* **[new]** Allows `update` blocks for `traverse` loops.
+
+### Libraries
+
+* **[new]** Adds `SpinlockMutex` to `lib/util`, for more efficient locking when
+  lockouts are rare or extremely short.
+
 ## 0.18.1.0  -- 2021-10-19
 
 ### Language
diff --git a/base/boxed.hpp b/base/boxed.hpp
--- a/base/boxed.hpp
+++ b/base/boxed.hpp
@@ -19,6 +19,8 @@
 #ifndef BOXED_HPP_
 #define BOXED_HPP_
 
+#include <cstddef>
+#include <cstdlib>
 #include <atomic>
 
 #include "function.hpp"
@@ -28,42 +30,41 @@
 namespace zeolite_internal {
 
 struct UnionValue {
-  // NOTE: Using enum class would break the switch/case logic.
-  // NOTE: These enum values assume that dynamic allocation will never be
-  // aligned to an odd address within a few bytes of ULLONG_MAX.
-  enum Type : unsigned long long {
-    EMPTY = ~0x00ULL,
-    BOOL  = ~0x02ULL,
-    CHAR  = ~0x04ULL,
-    INT   = ~0x06ULL,
-    FLOAT = ~0x08ULL,
+  enum class Type {
+    EMPTY,
+    BOOL,
+    CHAR,
+    INT,
+    FLOAT,
+    BOXED,
   };
 
-  struct Counters {
-    std::atomic_ullong strong_;
+  struct Pointer {
+    std::atomic_flag lock_;
+    std::atomic_int strong_;
     std::atomic_int weak_;
+    TypeValue* object_;
   };
 
-  union {
-    Counters* counters_;
-    Type value_type_;
-  } type_;
+  std::string CategoryName() const;
 
+  Type type_;
+
   union {
-    TypeValue* as_pointer_;
-    bool       as_bool_;
-    PrimChar   as_char_;
-    PrimInt    as_int_;
-    PrimFloat  as_float_;
+    unsigned char* as_bytes_;
+    Pointer*  as_pointer_;
+    bool      as_bool_;
+    PrimChar  as_char_;
+    PrimInt   as_int_;
+    PrimFloat as_float_;
   } value_;
 };
 
-}  // namespace zeolite_internal
 
-
 class BoxedValue {
  public:
-  BoxedValue();
+  constexpr BoxedValue()
+    : union_{ .type_ = UnionValue::Type::EMPTY, .value_ = { .as_pointer_ = nullptr } } {}
 
   BoxedValue(const BoxedValue&);
   BoxedValue& operator = (const BoxedValue&);
@@ -74,10 +75,23 @@
   BoxedValue(PrimChar value);
   BoxedValue(PrimInt value);
   BoxedValue(PrimFloat value);
-  BoxedValue(TypeValue* value);
 
+  template<class T, class... As>
+  static inline BoxedValue New(const As&... args) {
+    using Pointer = UnionValue::Pointer;
+    BoxedValue new_value;
+    new_value.union_.type_ = UnionValue::Type::BOXED;
+    new_value.union_.value_.as_bytes_ = (unsigned char*) malloc(sizeof(Pointer) + sizeof(T));
+    new (new_value.union_.value_.as_bytes_)
+      Pointer{ ATOMIC_FLAG_INIT, {1}, {1},
+               {new (new_value.union_.value_.as_bytes_ + sizeof(Pointer)) T(args...)} };
+    return new_value;
+  }
+
   ~BoxedValue();
 
+  void Validate(const std::string& name) const;
+
   bool AsBool() const;
   PrimChar AsChar() const;
   PrimInt AsInt() const;
@@ -91,21 +105,37 @@
   static BoxedValue Strong(const WeakValue& target);
 
  private:
-  friend class TypeValue;
+  friend class ::TypeValue;
   friend class WeakValue;
 
-  inline explicit BoxedValue(std::nullptr_t) : BoxedValue() {}
+  // Intentionally break old calls that used new.
+  inline explicit constexpr BoxedValue(void*) : BoxedValue() {}
 
+  inline explicit constexpr BoxedValue(std::nullptr_t) : BoxedValue() {}
+
   explicit BoxedValue(const WeakValue& other);
 
-  std::string CategoryName() const;
+  template<class T>
+  static inline BoxedValue FromPointer(T* pointer) {
+    BoxedValue value;
+    if (pointer) {
+      value.union_.type_ = UnionValue::Type::BOXED;
+      value.union_.value_.as_bytes_ =
+        reinterpret_cast<unsigned char*>(pointer)-sizeof(UnionValue::Pointer);
+      if (value.union_.value_.as_pointer_->object_ != pointer) {
+        FAIL() << "Bad VAR_SELF pointer";
+      }
+      ++value.union_.value_.as_pointer_->strong_;
+    }
+    return value;
+  }
 
-  ReturnTuple Dispatch(const BoxedValue& self, const ValueFunction& label,
-                       const ParamTuple& params, const ValueTuple& args) const;
+  ReturnTuple Dispatch(
+    const ValueFunction& label, const ParamTuple& params, const ValueTuple& args) const;
 
   void Cleanup();
 
-  zeolite_internal::UnionValue union_;
+  UnionValue union_;
 };
 
 
@@ -123,12 +153,19 @@
 
   ~WeakValue();
 
+  void Validate(const std::string& name) const;
+
  private:
   friend class BoxedValue;
 
   void Cleanup();
 
-  zeolite_internal::UnionValue union_;
+  UnionValue union_;
 };
+
+}  // namespace zeolite_internal
+
+using zeolite_internal::BoxedValue;
+using zeolite_internal::WeakValue;
 
 #endif  // BOXED_HPP_
diff --git a/base/category-source.hpp b/base/category-source.hpp
--- a/base/category-source.hpp
+++ b/base/category-source.hpp
@@ -98,7 +98,7 @@
   }
 
   static BoxedValue Reduce(const S<const TypeInstance>& from,
-                             const S<const TypeInstance>& to, BoxedValue target) {
+                           const S<const TypeInstance>& to, BoxedValue target) {
     TRACE_FUNCTION("reduce")
     return CanConvert(from, to)? target : Var_empty;
   }
@@ -154,10 +154,14 @@
   }
 };
 
+#define VAR_SELF TypeValue::Var_self(this)
+
 class TypeValue {
  public:
-  static ReturnTuple Call(const BoxedValue& target, const ValueFunction& label,
-                          const ParamTuple& params, const ValueTuple& args);
+  inline static ReturnTuple Call(const BoxedValue& target, const ValueFunction& label,
+                                 const ParamTuple& params, const ValueTuple& args) {
+    return target.Dispatch(label, params, args);
+  }
 
   virtual const PrimString& AsString() const;
   virtual PrimCharBuffer& AsCharBuffer();
@@ -167,14 +171,26 @@
 
  protected:
   friend class BoxedValue;
+  friend class zeolite_internal::UnionValue;
 
   TypeValue() = default;
 
+  template<class T>
+  inline static BoxedValue Var_self(T* must_be_this) {
+    return BoxedValue::FromPointer(must_be_this);
+  }
+
   // NOTE: For some reason, making this private causes a segfault.
   virtual std::string CategoryName() const = 0;
 
-  virtual ReturnTuple Dispatch(const BoxedValue& self, const ValueFunction& label,
-                               const ParamTuple& params, const ValueTuple& args);
+  virtual ReturnTuple Dispatch(
+    const ValueFunction& label, const ParamTuple& params, const ValueTuple& args);
+
+ private:
+  // Creating a BoxedValue from a TypeValue won't have the correct offset.
+  inline static BoxedValue Var_self(TypeValue* invalid) {
+    return BoxedValue();
+  }
 };
 
 class AnonymousOrder : public TypeValue {
@@ -186,14 +202,15 @@
                  const ValueFunction& func_get);
 
   std::string CategoryName() const final;
-  ReturnTuple Dispatch(const BoxedValue& self,
-                       const ValueFunction& label,
+  ReturnTuple Dispatch(const ValueFunction& label,
                        const ParamTuple& params,
                        const ValueTuple& args) final;
 
   virtual ~AnonymousOrder() = default;
 
  private:
+  // This *must* return VAR_SELF in the most-derived class!
+  virtual BoxedValue Var_self() = 0;
   virtual BoxedValue Call_next(const BoxedValue& self) = 0;
   virtual BoxedValue Call_get(const BoxedValue& self) = 0;
 
diff --git a/base/src/Extension_Bool.cpp b/base/src/Extension_Bool.cpp
--- a/base/src/Extension_Bool.cpp
+++ b/base/src/Extension_Bool.cpp
@@ -52,11 +52,11 @@
   }
 };
 
-ReturnTuple DispatchBool(bool value, const BoxedValue& Var_self, const ValueFunction& label,
+ReturnTuple DispatchBool(bool value, const ValueFunction& label,
                          const ParamTuple& params, const ValueTuple& args) {
   if (&label == &Function_AsBool_asBool) {
     TRACE_FUNCTION("Bool.asBool")
-    return ReturnTuple(Var_self);
+    return ReturnTuple(Box_Bool(value));
   }
   if (&label == &Function_AsFloat_asFloat) {
     TRACE_FUNCTION("Bool.asFloat")
diff --git a/base/src/Extension_Char.cpp b/base/src/Extension_Char.cpp
--- a/base/src/Extension_Char.cpp
+++ b/base/src/Extension_Char.cpp
@@ -72,7 +72,7 @@
   }
 };
 
-ReturnTuple DispatchChar(PrimChar value, const BoxedValue& Var_self, const ValueFunction& label,
+ReturnTuple DispatchChar(PrimChar value, const ValueFunction& label,
                          const ParamTuple& params, const ValueTuple& args) {
   if (&label == &Function_AsBool_asBool) {
     TRACE_FUNCTION("Char.asBool")
@@ -80,7 +80,7 @@
   }
   if (&label == &Function_AsChar_asChar) {
     TRACE_FUNCTION("Char.asChar")
-    return ReturnTuple(Var_self);
+    return ReturnTuple(Box_Char(value));
   }
   if (&label == &Function_AsFloat_asFloat) {
     TRACE_FUNCTION("Char.asFloat")
diff --git a/base/src/Extension_CharBuffer.cpp b/base/src/Extension_CharBuffer.cpp
--- a/base/src/Extension_CharBuffer.cpp
+++ b/base/src/Extension_CharBuffer.cpp
@@ -50,7 +50,7 @@
 struct ExtValue_CharBuffer : public Value_CharBuffer {
   inline ExtValue_CharBuffer(S<Type_CharBuffer> p, PrimCharBuffer value) : Value_CharBuffer(p), value_(std::move(value)) {}
 
-  ReturnTuple Call_readAt(const BoxedValue& Var_self, const ParamTuple& params, const ValueTuple& args) final {
+  ReturnTuple Call_readAt(const ParamTuple& params, const ValueTuple& args) final {
     TRACE_FUNCTION("CharBuffer.readAt")
     const PrimInt Var_arg1 = (args.At(0)).AsInt();
     if (Var_arg1 < 0 || Var_arg1 >= AsCharBuffer().size()) {
@@ -59,7 +59,7 @@
     return ReturnTuple(Box_Char(AsCharBuffer()[Var_arg1]));
   }
 
-  ReturnTuple Call_resize(const BoxedValue& Var_self, const ParamTuple& params, const ValueTuple& args) final {
+  ReturnTuple Call_resize(const ParamTuple& params, const ValueTuple& args) final {
     TRACE_FUNCTION("CharBuffer.resize")
     const PrimInt Var_arg1 = (args.At(0)).AsInt();
     if (Var_arg1 < 0) {
@@ -67,15 +67,15 @@
     } else {
       value_.resize(Var_arg1);
     }
-    return ReturnTuple(Var_self);
+    return ReturnTuple(VAR_SELF);
   }
 
-  ReturnTuple Call_size(const BoxedValue& Var_self, const ParamTuple& params, const ValueTuple& args) final {
+  ReturnTuple Call_size(const ParamTuple& params, const ValueTuple& args) final {
     TRACE_FUNCTION("CharBuffer.size")
     return ReturnTuple(Box_Int(value_.size()));
   }
 
-  ReturnTuple Call_writeAt(const BoxedValue& Var_self, const ParamTuple& params, const ValueTuple& args) final {
+  ReturnTuple Call_writeAt(const ParamTuple& params, const ValueTuple& args) final {
     TRACE_FUNCTION("CharBuffer.writeAt")
     const PrimInt Var_arg1 = (args.At(0)).AsInt();
     const PrimChar Var_arg2 = (args.At(1)).AsChar();
@@ -84,7 +84,7 @@
     } else {
       value_[Var_arg1] = Var_arg2;
     }
-    return ReturnTuple(Var_self);
+    return ReturnTuple(VAR_SELF);
   }
 
   PrimCharBuffer& AsCharBuffer() final { return value_; }
@@ -101,7 +101,7 @@
   return cached;
 }
 BoxedValue CreateValue_CharBuffer(S<Type_CharBuffer> parent, PrimCharBuffer value) {
-  return BoxedValue(new ExtValue_CharBuffer(parent, std::move(value)));
+  return BoxedValue::New<ExtValue_CharBuffer>(parent, std::move(value));
 }
 
 #ifdef ZEOLITE_PUBLIC_NAMESPACE
diff --git a/base/src/Extension_Float.cpp b/base/src/Extension_Float.cpp
--- a/base/src/Extension_Float.cpp
+++ b/base/src/Extension_Float.cpp
@@ -62,7 +62,7 @@
   }
 };
 
-ReturnTuple DispatchFloat(PrimFloat value, const BoxedValue& Var_self, const ValueFunction& label,
+ReturnTuple DispatchFloat(PrimFloat value, const ValueFunction& label,
                           const ParamTuple& params, const ValueTuple& args) {
   if (&label == &Function_AsBool_asBool) {
     TRACE_FUNCTION("Float.asBool")
@@ -70,7 +70,7 @@
   }
   if (&label == &Function_AsFloat_asFloat) {
     TRACE_FUNCTION("Float.asFloat")
-    return ReturnTuple(Var_self);
+    return ReturnTuple(Box_Float(value));
   }
   if (&label == &Function_AsInt_asInt) {
     TRACE_FUNCTION("Float.asInt")
diff --git a/base/src/Extension_Int.cpp b/base/src/Extension_Int.cpp
--- a/base/src/Extension_Int.cpp
+++ b/base/src/Extension_Int.cpp
@@ -75,7 +75,7 @@
   }
 };
 
-ReturnTuple DispatchInt(PrimInt value, const BoxedValue& Var_self, const ValueFunction& label,
+ReturnTuple DispatchInt(PrimInt value, const ValueFunction& label,
                         const ParamTuple& params, const ValueTuple& args) {
   if (&label == &Function_AsBool_asBool) {
     TRACE_FUNCTION("Int.asBool")
@@ -91,7 +91,7 @@
   }
   if (&label == &Function_AsInt_asInt) {
     TRACE_FUNCTION("Int.asInt")
-    return ReturnTuple(Var_self);
+    return ReturnTuple(Box_Int(value));
   }
   if (&label == &Function_Formatted_formatted) {
     TRACE_FUNCTION("Int.formatted")
diff --git a/base/src/Extension_String.cpp b/base/src/Extension_String.cpp
--- a/base/src/Extension_String.cpp
+++ b/base/src/Extension_String.cpp
@@ -47,8 +47,7 @@
  public:
   std::string CategoryName() const final { return "StringBuilder"; }
 
-  ReturnTuple Dispatch(const BoxedValue& self,
-                       const ValueFunction& label,
+  ReturnTuple Dispatch(const ValueFunction& label,
                        const ParamTuple& params, const ValueTuple& args) final {
     if (args.Size() != label.arg_count) {
       FAIL() << "Wrong number of args";
@@ -60,14 +59,14 @@
       TRACE_FUNCTION("StringBuilder.append")
       std::lock_guard<std::mutex> lock(mutex);
       output_ << args.At(0).AsString();
-      return ReturnTuple(self);
+      return ReturnTuple(VAR_SELF);
     }
     if (&label == &Function_Build_build) {
       TRACE_FUNCTION("StringBuilder.build")
       std::lock_guard<std::mutex> lock(mutex);
       return ReturnTuple(Box_String(output_.str()));
     }
-    return TypeValue::Dispatch(self, label, params, args);
+    return TypeValue::Dispatch(label, params, args);
   }
 
  private:
@@ -80,7 +79,7 @@
 
   ReturnTuple Call_builder(const S<TypeInstance>& Param_self, const ParamTuple& params, const ValueTuple& args) final {
     TRACE_FUNCTION("String.builder")
-    return ReturnTuple(BoxedValue(new Value_StringBuilder));
+    return ReturnTuple(BoxedValue::New<Value_StringBuilder>());
   }
 
   ReturnTuple Call_default(const S<TypeInstance>& Param_self, const ParamTuple& params, const ValueTuple& args) final {
@@ -109,10 +108,14 @@
   }
 };
 
-struct StringOrder : public AnonymousOrder {
+struct StringOrder final : public AnonymousOrder {
   StringOrder(BoxedValue container, const std::string& s)
     : AnonymousOrder(container, Function_Order_next, Function_Order_get), value(s) {}
 
+  BoxedValue Var_self() final {
+    return VAR_SELF;
+  }
+
   BoxedValue Call_next(const BoxedValue& self) final {
     if (index+1 >= value.size()) {
       return Var_empty;
@@ -134,26 +137,26 @@
 struct ExtValue_String : public Value_String {
   inline ExtValue_String(S<Type_String> p, const PrimString& value) : Value_String(p), value_(value) {}
 
-  ReturnTuple Call_asBool(const BoxedValue& Var_self, const ParamTuple& params, const ValueTuple& args) final {
+  ReturnTuple Call_asBool(const ParamTuple& params, const ValueTuple& args) final {
     TRACE_FUNCTION("String.asBool")
     return ReturnTuple(Box_Bool(value_.size() != 0));
   }
 
-  ReturnTuple Call_defaultOrder(const BoxedValue& Var_self, const ParamTuple& params, const ValueTuple& args) final {
+  ReturnTuple Call_defaultOrder(const ParamTuple& params, const ValueTuple& args) final {
     TRACE_FUNCTION("String.defaultOrder")
     if (value_.empty()) {
       return ReturnTuple(Var_empty);
     } else {
-      return ReturnTuple(BoxedValue(new StringOrder(Var_self, value_)));
+      return ReturnTuple(BoxedValue::New<StringOrder>(VAR_SELF, value_));
     }
   }
 
-  ReturnTuple Call_formatted(const BoxedValue& Var_self, const ParamTuple& params, const ValueTuple& args) final {
+  ReturnTuple Call_formatted(const ParamTuple& params, const ValueTuple& args) final {
     TRACE_FUNCTION("String.formatted")
-    return ReturnTuple(Var_self);
+    return ReturnTuple(VAR_SELF);
   }
 
-  ReturnTuple Call_readAt(const BoxedValue& Var_self, const ParamTuple& params, const ValueTuple& args) final {
+  ReturnTuple Call_readAt(const ParamTuple& params, const ValueTuple& args) final {
     TRACE_FUNCTION("String.readAt")
     const PrimInt Var_arg1 = (args.At(0)).AsInt();
     if (Var_arg1 < 0 || Var_arg1 >= value_.size()) {
@@ -162,12 +165,12 @@
     return ReturnTuple(Box_Char(value_[Var_arg1]));
   }
 
-  ReturnTuple Call_size(const BoxedValue& Var_self, const ParamTuple& params, const ValueTuple& args) final {
+  ReturnTuple Call_size(const ParamTuple& params, const ValueTuple& args) final {
     TRACE_FUNCTION("String.size")
     return ReturnTuple(Box_Int(value_.size()));
   }
 
-  ReturnTuple Call_subSequence(const BoxedValue& Var_self, const ParamTuple& params, const ValueTuple& args) final {
+  ReturnTuple Call_subSequence(const ParamTuple& params, const ValueTuple& args) final {
     TRACE_FUNCTION("String.subSequence")
     const PrimInt Var_arg1 = (args.At(0)).AsInt();
     const PrimInt Var_arg2 = (args.At(1)).AsInt();
@@ -200,5 +203,5 @@
 #endif  // ZEOLITE_PUBLIC_NAMESPACE
 
 BoxedValue Box_String(const PrimString& value) {
-  return BoxedValue(new ExtValue_String(CreateType_String(Params<0>::Type()), value));
+  return BoxedValue::New<ExtValue_String>(CreateType_String(Params<0>::Type()), value);
 }
diff --git a/base/src/boxed.cpp b/base/src/boxed.cpp
--- a/base/src/boxed.cpp
+++ b/base/src/boxed.cpp
@@ -27,16 +27,16 @@
 namespace ZEOLITE_PUBLIC_NAMESPACE {
 #endif  // ZEOLITE_PUBLIC_NAMESPACE
 
-ReturnTuple DispatchBool(bool value, const BoxedValue& Var_self, const ValueFunction& label,
+ReturnTuple DispatchBool(bool value, const ValueFunction& label,
                          const ParamTuple& params, const ValueTuple& args);
 
-ReturnTuple DispatchChar(PrimChar value, const BoxedValue& Var_self, const ValueFunction& label,
+ReturnTuple DispatchChar(PrimChar value, const ValueFunction& label,
                          const ParamTuple& params, const ValueTuple& args);
 
-ReturnTuple DispatchInt(PrimInt value, const BoxedValue& Var_self, const ValueFunction& label,
+ReturnTuple DispatchInt(PrimInt value, const ValueFunction& label,
                         const ParamTuple& params, const ValueTuple& args);
 
-ReturnTuple DispatchFloat(PrimFloat value, const BoxedValue& Var_self, const ValueFunction& label,
+ReturnTuple DispatchFloat(PrimFloat value, const ValueFunction& label,
                           const ParamTuple& params, const ValueTuple& args);
 
 #ifdef ZEOLITE_PUBLIC_NAMESPACE
@@ -45,24 +45,54 @@
 #endif  // ZEOLITE_PUBLIC_NAMESPACE
 
 
-using zeolite_internal::UnionValue;
+namespace zeolite_internal {
 
+namespace {
 
-BoxedValue::BoxedValue()
-  : union_{ { .value_type_ = UnionValue::Type::EMPTY },
-            { .as_pointer_ = nullptr } } {}
+void Validate(const std::string& name, const UnionValue& the_union) {
+  if (the_union.type_ == UnionValue::Type::BOXED) {
+    const auto strong = the_union.value_.as_pointer_->strong_.load();
+    const auto weak   = the_union.value_.as_pointer_->weak_.load();
+    const TypeValue* const object = the_union.value_.as_pointer_->object_;
 
+    if ((strong == 0 && object) || (weak == 0 && strong > 0)) {
+      FAIL() << "Leaked " << the_union.CategoryName() << " " << name << " at "
+             << ((void*) the_union.value_.as_pointer_) << " (S: "
+             << std::hex << strong << " W: " << weak << ")";
+    }
+
+    if ((strong != 0 && !object)) {
+      FAIL() << "Invalid counts for freed value " << name << " at "
+             << ((void*) the_union.value_.as_pointer_) << " (S: "
+             << std::hex << strong << " W: " << weak << ")";
+    }
+  }
+}
+
+}  // namespace
+
+std::string UnionValue::CategoryName() const {
+  switch (type_) {
+    case UnionValue::Type::EMPTY: return "empty";
+    case UnionValue::Type::BOOL:  return "Bool";
+    case UnionValue::Type::CHAR:  return "Char";
+    case UnionValue::Type::INT:   return "Int";
+    case UnionValue::Type::FLOAT: return "Float";
+    case UnionValue::Type::BOXED:
+      if (!value_.as_pointer_ || !value_.as_pointer_->object_) {
+        FAIL() << "Function called on null pointer";
+      }
+      return value_.as_pointer_->object_->CategoryName();
+  }
+}
+
 BoxedValue::BoxedValue(const BoxedValue& other)
   : union_(other.union_) {
-  switch (union_.type_.value_type_) {
-    case UnionValue::Type::EMPTY:
-    case UnionValue::Type::BOOL:
-    case UnionValue::Type::CHAR:
-    case UnionValue::Type::INT:
-    case UnionValue::Type::FLOAT:
+  switch (union_.type_) {
+    case UnionValue::Type::BOXED:
+      ++union_.value_.as_pointer_->strong_;
       break;
     default:
-      ++union_.type_.counters_->strong_;
       break;
   }
 }
@@ -71,15 +101,11 @@
   if (&other != this) {
     Cleanup();
     union_ = other.union_;
-    switch (union_.type_.value_type_) {
-      case UnionValue::Type::EMPTY:
-      case UnionValue::Type::BOOL:
-      case UnionValue::Type::CHAR:
-      case UnionValue::Type::INT:
-      case UnionValue::Type::FLOAT:
+    switch (union_.type_) {
+      case UnionValue::Type::BOXED:
+        ++union_.value_.as_pointer_->strong_;
         break;
       default:
-        ++union_.type_.counters_->strong_;
         break;
     }
   }
@@ -88,7 +114,7 @@
 
 BoxedValue::BoxedValue(BoxedValue&& other)
   : union_(other.union_) {
-  other.union_.type_.value_type_  = UnionValue::Type::EMPTY;
+  other.union_.type_  = UnionValue::Type::EMPTY;
   other.union_.value_.as_pointer_ = nullptr;
 }
 
@@ -96,7 +122,7 @@
   if (&other != this) {
     Cleanup();
     union_ = other.union_;
-    other.union_.type_.value_type_  = UnionValue::Type::EMPTY;
+    other.union_.type_  = UnionValue::Type::EMPTY;
     other.union_.value_.as_pointer_ = nullptr;
   }
   return *this;
@@ -104,145 +130,130 @@
 
 BoxedValue::BoxedValue(const WeakValue& other)
   : union_(other.union_) {
-  switch (union_.type_.value_type_) {
-    case UnionValue::Type::EMPTY:
-    case UnionValue::Type::BOOL:
-    case UnionValue::Type::CHAR:
-    case UnionValue::Type::INT:
-    case UnionValue::Type::FLOAT:
-      break;
-    default:
-      // Using the top 24 bits here allows blocking the deletion of the pointer
-      // without risking other threads using a bad pointer. This assumes that
-      // each object will have fewer than 2^40 references, and that fewer than
-      // 2^24 threads will be attempting to lock a weak reference for a given
-      // object at any one time.
-      static constexpr unsigned long long strong_lock = 0x1ULL << 40;
-      if (union_.type_.counters_->strong_.fetch_add(strong_lock) % strong_lock == 0) {
-        // NOTE: Subtraction of strong_lock *cannot* be optimized out!
-        //
-        // Unsigned overflow would still leave strong_%strong_lock == 0, but
-        // there could be a race-condition between three threads:
-        //
-        // Thread 1: Enters *this* constructor while the pointer is still valid.
-        // Thread 2: Enters BoxedValue::Cleanup and removes the last reference.
-        // Thread 3: Enters *this* constructor before Thread 1 subtracts
-        //           strong_lock-1, meaning strong_%strong_lock == 0.
-        // Thread 1: Subtracts strong_lock-1 (+1 overall) to revive the pointer.
-        //
-        // This still leaves all three threads in a valid state, but with Thread
-        // 3 getting empty instead of a still-valid pointer. In other words,
-        // strong_%strong_lock == 0 *doesn't* guarantee that the pointer is no
-        // longer valid.
-        union_.type_.counters_->strong_.fetch_sub(strong_lock);
-        union_.type_.value_type_  = UnionValue::Type::EMPTY;
+  switch (union_.type_) {
+    case UnionValue::Type::BOXED:
+      while (union_.value_.as_pointer_->lock_.test_and_set(std::memory_order_acquire));
+      if (++union_.value_.as_pointer_->strong_ == 1) {
+        --union_.value_.as_pointer_->strong_;
+        union_.value_.as_pointer_->lock_.clear(std::memory_order_release);
+        union_.type_ = UnionValue::Type::EMPTY;
         union_.value_.as_pointer_ = nullptr;
       } else {
-        union_.type_.counters_->strong_.fetch_sub(strong_lock-1);
+        union_.value_.as_pointer_->lock_.clear(std::memory_order_release);
       }
       break;
+    default:
+      break;
   }
 }
 
 BoxedValue::BoxedValue(bool value)
-  : union_{ { .value_type_ = UnionValue::Type::BOOL },
-            { .as_bool_ = value } } {}
+  : union_{ .type_ = UnionValue::Type::BOOL, .value_ = { .as_bool_ = value } } {}
 
 BoxedValue::BoxedValue(PrimChar value)
-  : union_{ { .value_type_ = UnionValue::Type::CHAR },
-            { .as_char_= value } } {}
+  : union_{ .type_ = UnionValue::Type::CHAR, .value_ = { .as_char_ = value } } {}
 
 BoxedValue::BoxedValue(PrimInt value)
-  : union_{ { .value_type_ = UnionValue::Type::INT },
-            { .as_int_= value } } {}
+  : union_{ .type_ = UnionValue::Type::INT, .value_ = { .as_int_ = value } } {}
 
 BoxedValue::BoxedValue(PrimFloat value)
-  : union_{ { .value_type_ = UnionValue::Type::FLOAT },
-            { .as_float_ = value } } {}
-
-BoxedValue::BoxedValue(TypeValue* value)
-  : union_{ { .counters_ = new UnionValue::Counters{{1},{1}} },
-            { .as_pointer_ = value } } {}
+  : union_{ .type_ = UnionValue::Type::FLOAT, .value_ = { .as_float_ = value } } {}
 
 BoxedValue::~BoxedValue() {
   Cleanup();
 }
 
+void BoxedValue::Validate(const std::string& name) const {
+  zeolite_internal::Validate(name, union_);
+}
+
 bool BoxedValue::AsBool() const {
-  if (union_.type_.value_type_ != UnionValue::Type::BOOL) {
-    FAIL() << CategoryName() << " is not a Bool value";
+  switch (union_.type_) {
+    case UnionValue::Type::BOOL:
+      return union_.value_.as_bool_;
+    default:
+      FAIL() << union_.CategoryName() << " is not a Bool value";
+      __builtin_unreachable();
+      break;
   }
-  return union_.value_.as_bool_;
 }
 
 PrimChar BoxedValue::AsChar() const {
-  if (union_.type_.value_type_ != UnionValue::Type::CHAR) {
-    FAIL() << CategoryName() << " is not a Char value";
+  switch (union_.type_) {
+    case UnionValue::Type::CHAR:
+      return union_.value_.as_char_;
+    default:
+      FAIL() << union_.CategoryName() << " is not a Char value";
+      __builtin_unreachable();
+      break;
   }
-  return union_.value_.as_char_;
 }
 
 PrimInt BoxedValue::AsInt() const {
-  if (union_.type_.value_type_ != UnionValue::Type::INT) {
-    FAIL() << CategoryName() << " is not an Int value";
+  switch (union_.type_) {
+    case UnionValue::Type::INT:
+      return union_.value_.as_int_;
+    default:
+      FAIL() << union_.CategoryName() << " is not an Int value";
+      __builtin_unreachable();
+      break;
   }
-  return union_.value_.as_int_;
 }
 
 PrimFloat BoxedValue::AsFloat() const {
-  if (union_.type_.value_type_ != UnionValue::Type::FLOAT) {
-    FAIL() << CategoryName() << " is not a Float value";
+  switch (union_.type_) {
+    case UnionValue::Type::FLOAT:
+      return union_.value_.as_float_;
+    default:
+      FAIL() << union_.CategoryName() << " is not a Float value";
+      __builtin_unreachable();
+      break;
   }
-  return union_.value_.as_float_;
 }
 
 const PrimString& BoxedValue::AsString() const {
-  switch (union_.type_.value_type_) {
-    case UnionValue::Type::EMPTY:
-    case UnionValue::Type::BOOL:
-    case UnionValue::Type::CHAR:
-    case UnionValue::Type::INT:
-    case UnionValue::Type::FLOAT:
-      FAIL() << CategoryName() << " is not a String value";
-      __builtin_unreachable();
-      break;
-    default:
-      if (!union_.value_.as_pointer_) {
+  switch (union_.type_) {
+    case UnionValue::Type::BOXED:
+      if (!union_.value_.as_pointer_ || !union_.value_.as_pointer_->object_) {
         FAIL() << "Function called on null pointer";
       }
-      return union_.value_.as_pointer_->AsString();
+      return union_.value_.as_pointer_->object_->AsString();
+    default:
+      FAIL() << union_.CategoryName() << " is not a String value";
+      __builtin_unreachable();
+      break;
   }
 }
 
 PrimCharBuffer& BoxedValue::AsCharBuffer() const {
-  switch (union_.type_.value_type_) {
-    case UnionValue::Type::EMPTY:
-    case UnionValue::Type::BOOL:
-    case UnionValue::Type::CHAR:
-    case UnionValue::Type::INT:
-    case UnionValue::Type::FLOAT:
-      FAIL() << CategoryName() << " is not a CharBuffer value";
-      __builtin_unreachable();
-      break;
-    default:
-      if (!union_.value_.as_pointer_) {
+  switch (union_.type_) {
+    case UnionValue::Type::BOXED:
+      if (!union_.value_.as_pointer_ || !union_.value_.as_pointer_->object_) {
         FAIL() << "Function called on null pointer";
       }
-      return union_.value_.as_pointer_->AsCharBuffer();
+      return union_.value_.as_pointer_->object_->AsCharBuffer();
+    default:
+      FAIL() << union_.CategoryName() << " is not a CharBuffer value";
+      __builtin_unreachable();
+      break;
   }
 }
 
 // static
 bool BoxedValue::Present(const BoxedValue& target) {
-  return target.union_.type_.value_type_ != UnionValue::Type::EMPTY;
+  return target.union_.type_ != UnionValue::Type::EMPTY;
 }
 
 // static
 BoxedValue BoxedValue::Require(const BoxedValue& target) {
-  if (target.union_.type_.value_type_ == UnionValue::Type::EMPTY) {
-    FAIL() << "Cannot require empty value";
+  switch (target.union_.type_) {
+    case UnionValue::Type::EMPTY:
+      FAIL() << "Cannot require empty value";
+      __builtin_unreachable();
+      break;
+    default:
+      return target;
   }
-  return target;
 }
 
 // static
@@ -250,82 +261,64 @@
   return BoxedValue(target);
 }
 
-std::string BoxedValue::CategoryName() const {
-  switch (union_.type_.value_type_) {
-    case UnionValue::Type::EMPTY: return "empty";
-    case UnionValue::Type::BOOL:  return "Bool";
-    case UnionValue::Type::CHAR:  return "Char";
-    case UnionValue::Type::INT:   return "Int";
-    case UnionValue::Type::FLOAT: return "Float";
-    default:
-      if (!union_.value_.as_pointer_) {
-        FAIL() << "Function called on null pointer";
-      }
-      return union_.value_.as_pointer_->CategoryName();
-  }
-}
-
 ReturnTuple BoxedValue::Dispatch(
-  const BoxedValue& self, const ValueFunction& label,
-  const ParamTuple& params, const ValueTuple& args) const {
-  switch (union_.type_.value_type_) {
+  const ValueFunction& label, const ParamTuple& params, const ValueTuple& args) const {
+  switch (union_.type_) {
     case UnionValue::Type::EMPTY:
       FAIL() << "Function called on empty value";
       __builtin_unreachable();
       break;
     case UnionValue::Type::BOOL:
-      return DispatchBool(union_.value_.as_bool_, self, label, params, args);
+      return DispatchBool(union_.value_.as_bool_, label, params, args);
     case UnionValue::Type::CHAR:
-      return DispatchChar(union_.value_.as_char_, self, label, params, args);
+      return DispatchChar(union_.value_.as_char_, label, params, args);
     case UnionValue::Type::INT:
-      return DispatchInt(union_.value_.as_int_, self, label, params, args);
+      return DispatchInt(union_.value_.as_int_, label, params, args);
     case UnionValue::Type::FLOAT:
-      return DispatchFloat(union_.value_.as_float_, self, label, params, args);
-    default:
-      if (!union_.value_.as_pointer_) {
+      return DispatchFloat(union_.value_.as_float_, label, params, args);
+    case UnionValue::Type::BOXED:
+      if (!union_.value_.as_pointer_ || !union_.value_.as_pointer_->object_) {
         FAIL() << "Function called on null pointer";
       }
-      return union_.value_.as_pointer_->Dispatch(self, label, params, args);
+      return union_.value_.as_pointer_->object_->Dispatch(label, params, args);
   }
 }
 
 void BoxedValue::Cleanup() {
-  switch (union_.type_.value_type_) {
-    case UnionValue::Type::EMPTY:
-    case UnionValue::Type::BOOL:
-    case UnionValue::Type::CHAR:
-    case UnionValue::Type::INT:
-    case UnionValue::Type::FLOAT:
-      break;
-    default:
-      if (--union_.type_.counters_->strong_ == 0) {
-        delete union_.value_.as_pointer_;
-        if (--union_.type_.counters_->weak_ == 0) {
-          delete union_.type_.counters_;
+  switch (union_.type_) {
+    case UnionValue::Type::BOXED:
+      while (union_.value_.as_pointer_->lock_.test_and_set(std::memory_order_acquire));
+      if (--union_.value_.as_pointer_->strong_ == 0) {
+        TypeValue* const object = union_.value_.as_pointer_->object_;
+        union_.value_.as_pointer_->object_ = nullptr;
+        union_.value_.as_pointer_->lock_.clear(std::memory_order_release);
+        object->~TypeValue();
+        if (--union_.value_.as_pointer_->weak_ == 0) {
+          // NOTE: as_bytes_ contains object => ~TypeValue() must happen first.
+          free(union_.value_.as_bytes_);
         }
+      } else {
+        union_.value_.as_pointer_->lock_.clear(std::memory_order_release);
       }
       break;
+    default:
+      break;
   }
-  union_.type_.value_type_  = UnionValue::Type::EMPTY;
+  union_.type_  = UnionValue::Type::EMPTY;
   union_.value_.as_pointer_ = nullptr;
 }
 
 
 WeakValue::WeakValue()
-  : union_{ { .value_type_ = UnionValue::Type::EMPTY },
-            { .as_pointer_ = nullptr } } {}
+  : union_{ .type_ = UnionValue::Type::EMPTY, .value_ = { .as_pointer_ = nullptr } } {}
 
 WeakValue::WeakValue(const WeakValue& other)
   : union_(other.union_) {
-  switch (union_.type_.value_type_) {
-    case UnionValue::Type::EMPTY:
-    case UnionValue::Type::BOOL:
-    case UnionValue::Type::CHAR:
-    case UnionValue::Type::INT:
-    case UnionValue::Type::FLOAT:
+  switch (union_.type_) {
+    case UnionValue::Type::BOXED:
+      ++union_.value_.as_pointer_->weak_;
       break;
     default:
-      ++union_.type_.counters_->weak_;
       break;
   }
 }
@@ -334,15 +327,11 @@
   if (&other != this) {
     Cleanup();
     union_ = other.union_;
-    switch (union_.type_.value_type_) {
-      case UnionValue::Type::EMPTY:
-      case UnionValue::Type::BOOL:
-      case UnionValue::Type::CHAR:
-      case UnionValue::Type::INT:
-      case UnionValue::Type::FLOAT:
+    switch (union_.type_) {
+      case UnionValue::Type::BOXED:
+        ++union_.value_.as_pointer_->weak_;
         break;
       default:
-        ++union_.type_.counters_->weak_;
         break;
     }
   }
@@ -351,7 +340,7 @@
 
 WeakValue::WeakValue(WeakValue&& other)
   : union_(other.union_) {
-  other.union_.type_.value_type_  = UnionValue::Type::EMPTY;
+  other.union_.type_  = UnionValue::Type::EMPTY;
   other.union_.value_.as_pointer_ = nullptr;
 }
 
@@ -359,7 +348,7 @@
   if (&other != this) {
     Cleanup();
     union_ = other.union_;
-    other.union_.type_.value_type_  = UnionValue::Type::EMPTY;
+    other.union_.type_  = UnionValue::Type::EMPTY;
     other.union_.value_.as_pointer_ = nullptr;
   }
   return *this;
@@ -367,15 +356,11 @@
 
 WeakValue::WeakValue(const BoxedValue& other)
   : union_(other.union_) {
-  switch (union_.type_.value_type_) {
-    case UnionValue::Type::EMPTY:
-    case UnionValue::Type::BOOL:
-    case UnionValue::Type::CHAR:
-    case UnionValue::Type::INT:
-    case UnionValue::Type::FLOAT:
+  switch (union_.type_) {
+    case UnionValue::Type::BOXED:
+      ++union_.value_.as_pointer_->weak_;
       break;
     default:
-      ++union_.type_.counters_->weak_;
       break;
   }
 }
@@ -383,15 +368,11 @@
 WeakValue& WeakValue::operator = (const BoxedValue& other) {
   Cleanup();
   union_ = other.union_;
-  switch (union_.type_.value_type_) {
-    case UnionValue::Type::EMPTY:
-    case UnionValue::Type::BOOL:
-    case UnionValue::Type::CHAR:
-    case UnionValue::Type::INT:
-    case UnionValue::Type::FLOAT:
+  switch (union_.type_) {
+    case UnionValue::Type::BOXED:
+      ++union_.value_.as_pointer_->weak_;
       break;
     default:
-      ++union_.type_.counters_->weak_;
       break;
   }
   return *this;
@@ -401,20 +382,22 @@
   Cleanup();
 }
 
+void WeakValue::Validate(const std::string& name) const {
+  zeolite_internal::Validate(name, union_);
+}
+
 void WeakValue::Cleanup() {
-  switch (union_.type_.value_type_) {
-    case UnionValue::Type::EMPTY:
-    case UnionValue::Type::BOOL:
-    case UnionValue::Type::CHAR:
-    case UnionValue::Type::INT:
-    case UnionValue::Type::FLOAT:
+  switch (union_.type_) {
+    case UnionValue::Type::BOXED:
+      if (--union_.value_.as_pointer_->weak_ == 0) {
+        free(union_.value_.as_bytes_);
+      }
       break;
     default:
-      if (--union_.type_.counters_->weak_ == 0) {
-        delete union_.type_.counters_;
-      }
       break;
   }
-  union_.type_.value_type_  = UnionValue::Type::EMPTY;
+  union_.type_  = UnionValue::Type::EMPTY;
   union_.value_.as_pointer_ = nullptr;
 }
+
+}  // namespace zeolite_internal
diff --git a/base/src/category-source.cpp b/base/src/category-source.cpp
--- a/base/src/category-source.cpp
+++ b/base/src/category-source.cpp
@@ -136,7 +136,7 @@
   __builtin_unreachable();
 }
 
-ReturnTuple TypeValue::Dispatch(const BoxedValue& self, const ValueFunction& label,
+ReturnTuple TypeValue::Dispatch(const ValueFunction& label,
                                 const ParamTuple& params, const ValueTuple& args) {
   FAIL() << CategoryName() << " does not implement " << label;
   __builtin_unreachable();
@@ -205,12 +205,6 @@
   }
 }
 
-// static
-ReturnTuple TypeValue::Call(const BoxedValue& target, const ValueFunction& label,
-                            const ParamTuple& params, const ValueTuple& args) {
-  return target.Dispatch(target, label, params, args);
-}
-
 const PrimString& TypeValue::AsString() const {
   FAIL() << CategoryName() << " is not a String value";
   __builtin_unreachable();
@@ -229,15 +223,14 @@
 std::string AnonymousOrder::CategoryName() const { return "AnonymousOrder"; }
 
 ReturnTuple AnonymousOrder::Dispatch(
-  const BoxedValue& self, const ValueFunction& label,
-  const ParamTuple& params, const ValueTuple& args) {
+  const ValueFunction& label, const ParamTuple& params, const ValueTuple& args) {
   if (&label == &function_next) {
     TRACE_FUNCTION("AnonymousOrder.next")
-    return ReturnTuple(Call_next(self));
+    return ReturnTuple(Call_next(Var_self()));
   }
   if (&label == &function_get) {
     TRACE_FUNCTION("AnonymousOrder.get")
-    return ReturnTuple(Call_get(self));
+    return ReturnTuple(Call_get(Var_self()));
   }
-  return TypeValue::Dispatch(self, label, params, args);
+  return TypeValue::Dispatch(label, params, args);
 }
diff --git a/base/src/logging.cpp b/base/src/logging.cpp
--- a/base/src/logging.cpp
+++ b/base/src/logging.cpp
@@ -18,6 +18,7 @@
 
 #include "logging.hpp"
 
+#include <atomic>
 #include <chrono>
 #include <csignal>
 #include <iomanip>
@@ -31,8 +32,13 @@
     : fail_(fail), signal_(signal) {}
 
 LogThenCrash::~LogThenCrash() {
+  static std::atomic_int waiting_count{0};
+  static std::atomic_flag print_lock = ATOMIC_FLAG_INIT;
   if (fail_) {
     std::signal(signal_, SIG_DFL);
+    ++waiting_count;
+    while (print_lock.test_and_set(std::memory_order_acquire));
+    --waiting_count;
     if (Argv::ArgCount() > 0) {
       std::cerr << Argv::GetArgAt(0) << ": ";
     }
@@ -47,7 +53,10 @@
       std::cerr << "Original " << TraceCreation::GetType() << " value creation:" << std::endl;
       PrintTrace(creation_trace);
     }
-    std::raise(signal_);
+    if (!waiting_count.load()) {
+      std::raise(signal_);
+    }
+    print_lock.clear(std::memory_order_release);
   }
 }
 
diff --git a/base/types.hpp b/base/types.hpp
--- a/base/types.hpp
+++ b/base/types.hpp
@@ -117,9 +117,15 @@
 class TypeCategory;
 class TypeInstance;
 class TypeValue;
+
+namespace zeolite_internal {
 class BoxedValue;
 class WeakValue;
+}  // namespace zeolite_internal
 
+using zeolite_internal::BoxedValue;
+using zeolite_internal::WeakValue;
+
 template<int N, class...Ts>
 struct Params {
   using Type = typename Params<N-1, const S<TypeInstance>&, Ts...>::Type;
@@ -229,6 +235,8 @@
 
 class ReturnTuple : public ValueTuple {
  public:
+  constexpr ReturnTuple() : size_(0), data_() {}
+
   ReturnTuple(int size) : size_(size), data_(size_) {}
 
   template<class...Ts>
@@ -255,6 +263,8 @@
 
 class ArgTuple : public ValueTuple {
  public:
+  constexpr ArgTuple() : size_(0), data_() {}
+
   template<class...Ts>
   explicit ArgTuple(const Ts&... args) : size_(sizeof...(Ts)), data_(size_) {
     data_.Init(&args...);
@@ -277,6 +287,8 @@
 
 class ParamTuple {
  public:
+  constexpr ParamTuple() : size_(0), data_() {}
+
   template<class...Ts>
   explicit ParamTuple(const Ts&... params) : size_(sizeof...(Ts)), data_(size_) {
     data_.Init(std::move(params)...);
diff --git a/bin/zeolite-setup.hs b/bin/zeolite-setup.hs
--- a/bin/zeolite-setup.hs
+++ b/bin/zeolite-setup.hs
@@ -32,13 +32,12 @@
 
 main :: IO ()
 main = do
-  args <- getArgs
-  when (not $ null args) $ hPutStrLn stderr $ "Ignoring extra arguments: " ++ show args
+  (cxxSpec:arSpec:_) <- fmap ((++ repeat Nothing) . map Just) getArgs
   f <- localConfigPath
   isFile <- doesFileExist f
   when isFile $ do
     hPutStrLn stderr $ "*** WARNING: Local config " ++ f ++ " will be overwritten. ***"
-  config <- createConfig
+  config <- createConfig cxxSpec arSpec
   hPutStrLn stderr $ "Writing local config to " ++ f ++ "."
   writeFile f (show config ++ "\n")
   initLibraries config
@@ -81,13 +80,18 @@
 binaryFlags :: [String]
 binaryFlags = ["-O2", "-std=c++11"]
 
-createConfig :: IO LocalConfig
-createConfig = do
+intOrString :: String -> Either Int String
+intOrString s = handle (reads s :: [(Int, String)]) where
+  handle [(n,"")] = Left n
+  handle _        = Right s
+
+createConfig :: Maybe String -> Maybe String -> IO LocalConfig
+createConfig cxxSpec arSpec = do
   clang <- findExecutables clangBinary
   gcc   <- findExecutables gccBinary
   ar    <- findExecutables arBinary
-  compiler <- promptChoice "Which clang-compatible C++ compiler should be used?" (clang ++ gcc)
-  archiver <- promptChoice "Which ar-compatible archiver should be used?" ar
+  compiler <- promptChoice "Which clang-compatible C++ compiler should be used?" cxxSpec (clang ++ gcc)
+  archiver <- promptChoice "Which ar-compatible archiver should be used?"        arSpec  ar
   let config = LocalConfig {
       lcBackend = UnixBackend {
         ucCxxBinary    = compiler,
@@ -103,8 +107,15 @@
     }
   return config
 
-promptChoice :: String -> [String] -> IO String
-promptChoice p cs = do
+promptChoice :: String -> Maybe String -> [String] -> IO String
+promptChoice _ (Just spec) cs = handle $ intOrString spec where
+  handle (Right s) = return s
+  handle (Left n)
+    | n < 1 || n > length cs = do
+      hPutStrLn stderr $ "Index " ++ show n ++ " is out of bounds for " ++ show cs
+      exitFailure
+    | otherwise = return $ cs !! (n-1)
+promptChoice p _ cs = do
   n <- getChoice
   if n <= length cs
      then return $ cs !! (n-1)
diff --git a/example/random/.zeolite-module b/example/random/.zeolite-module
new file mode 100644
--- /dev/null
+++ b/example/random/.zeolite-module
@@ -0,0 +1,12 @@
+root: "../.."
+path: "example/random"
+private_deps: [
+  "lib/util"
+  "lib/math"
+  "lib/thread"
+]
+mode: binary {
+  category: RandomDemo
+  function: run
+  link_mode: static
+}
diff --git a/example/random/README.md b/example/random/README.md
new file mode 100644
--- /dev/null
+++ b/example/random/README.md
@@ -0,0 +1,17 @@
+# Zeolite Random Example
+
+*Also see
+[a highlighted version of the example code](https://ta0kira.github.io/zeolite/example/random).*
+
+To run the example:
+
+```shell
+# This is just to locate the example code. Not for normal use!
+ZEOLITE_PATH=$(zeolite --get-path)
+
+# Compile the example.
+zeolite -p "$ZEOLITE_PATH" -r example/random
+
+# Execute the compiled binary.
+$ZEOLITE_PATH/example/random/RandomDemo
+```
diff --git a/example/random/random-demo.0rx b/example/random/random-demo.0rx
new file mode 100644
--- /dev/null
+++ b/example/random/random-demo.0rx
@@ -0,0 +1,75 @@
+concrete RandomDemo {
+  @type run () -> ()
+}
+
+define RandomDemo {
+  run () {
+    Float timeLimit <- 0.0
+    Float startTime <- Realtime.monoSeconds()
+    if (Argv.global().size() > 1) {
+      timeLimit <- ParseChars.float(Argv.global().readAt(1)).getValue()
+    }
+
+    // Rough distribution of letter frequencies in the English language.
+    // Taken from https://en.wikipedia.org/wiki/Letter_frequency.
+    // NOTE: The overall scale here doesn't matter; it just matters what the
+    // relative weights are. CategoricalTree only supports positive Int weights.
+    CategoricalTree<Char> weights <- CategoricalTree<Char>.new()
+        .setWeight('a',8167)
+        .setWeight('b',1492)
+        .setWeight('c',2782)
+        .setWeight('d',4253)
+        .setWeight('e',12702)
+        .setWeight('f',2228)
+        .setWeight('g',2015)
+        .setWeight('h',6094)
+        .setWeight('i',6966)
+        .setWeight('j',153)
+        .setWeight('k',772)
+        .setWeight('l',4025)
+        .setWeight('m',2406)
+        .setWeight('n',6749)
+        .setWeight('o',7507)
+        .setWeight('p',1929)
+        .setWeight('q',95)
+        .setWeight('r',5987)
+        .setWeight('s',6327)
+        .setWeight('t',9056)
+        .setWeight('u',2758)
+        .setWeight('v',978)
+        .setWeight('w',2361)
+        .setWeight('x',150)
+        .setWeight('y',1974)
+        .setWeight('z',74)
+
+    // We could use weights.getTotal() as the upper limit here, but setting it
+    // to 1.0 allows us to account for changes to weights.
+    Generator<Float> letter <- RandomUniform.new(0.0,1.0)
+    Generator<Float> length <- RandomGaussian.new(5.0,3.0)
+    Generator<Float> timing <- RandomExponential.new(4.0)
+
+    while (timeLimit <= 0.0 || Realtime.monoSeconds() < startTime+timeLimit) {
+      // Generate a word with a random length.
+      Int wordLength <- length.generate().asInt()
+      if (wordLength < 1) {
+        continue
+      }
+      CharBuffer buffer <- CharBuffer.new(wordLength)
+
+      // Populate the letters using their relative frequencies.
+      traverse (Counter.zeroIndexed(buffer.size()) -> Int pos) {
+        Int offset <- (weights.getTotal().asFloat()*letter.generate()).asInt()
+        \ buffer.writeAt(pos,weights.locate(offset))
+      }
+
+      // Print the word.
+      \ LazyStream<Formatted>.new()
+          .append(String.fromCharBuffer(buffer))
+          .append("\n")
+          .writeTo(SimpleOutput.stdout())
+
+      // Wait a random amount of time.
+      \ Realtime.sleepSeconds(timing.generate())
+    }
+  }
+}
diff --git a/lib/container/auto-tree.0rp b/lib/container/auto-tree.0rp
--- a/lib/container/auto-tree.0rp
+++ b/lib/container/auto-tree.0rp
@@ -35,8 +35,10 @@
 //   without risking modification of the tree.
 concrete AutoBinaryTree<|#n,#k,#v|#r> {
   refines Container
+  refines Duplicate
   #n defines  KVFactory<#k,#v>
   #n requires BalancedTreeNode<#n,#k,#v>
+  #n requires Duplicate
   #k defines  LessThan<#k>
   #r allows   #n
 
diff --git a/lib/container/auto-tree.0rx b/lib/container/auto-tree.0rx
--- a/lib/container/auto-tree.0rx
+++ b/lib/container/auto-tree.0rx
@@ -28,6 +28,14 @@
     return treeSize
   }
 
+  duplicate () {
+    if (present(root)) {
+      return #self{ treeSize, require(root).duplicate() }
+    } else {
+      return #self{ 0, empty }
+    }
+  }
+
   getRoot () {
     return root
   }
diff --git a/lib/container/search-tree.0rp b/lib/container/search-tree.0rp
--- a/lib/container/search-tree.0rp
+++ b/lib/container/search-tree.0rp
@@ -24,6 +24,7 @@
 concrete SearchTree<#k,#v> {
   refines Container
   refines DefaultOrder<KeyValue<#k,#v>>
+  refines Duplicate
   refines KVWriter<#k,#v>
   refines KVReader<#k,#v>
   #k defines LessThan<#k>
diff --git a/lib/container/search-tree.0rt b/lib/container/search-tree.0rt
--- a/lib/container/search-tree.0rt
+++ b/lib/container/search-tree.0rt
@@ -186,3 +186,17 @@
     fail("Failed")
   }
 }
+
+unittest duplicate {
+  SearchTree<Int,Int> tree <- SearchTree<Int,Int>.new().set(1,1).set(2,2)
+
+  SearchTree<Int,Int> copy <- tree.duplicate()
+  \ Testing.checkEquals<?>(copy.size(),2)
+  \ Testing.checkOptional<?>(copy.get(1),1)
+  \ Testing.checkOptional<?>(copy.get(2),2)
+
+  \ copy.remove(2)
+  \ Testing.checkEquals<?>(copy.size(),1)
+  \ Testing.checkOptional<?>(copy.get(1),1)
+  \ Testing.checkOptional<?>(tree.get(2),2)
+}
diff --git a/lib/container/search-tree.0rx b/lib/container/search-tree.0rx
--- a/lib/container/search-tree.0rx
+++ b/lib/container/search-tree.0rx
@@ -27,6 +27,10 @@
     return tree.size()
   }
 
+  duplicate () {
+    return #self{ tree.duplicate() }
+  }
+
   set (k,v) {
     \ tree.set(k,v)
     return self
@@ -135,6 +139,7 @@
 concrete SearchTreeNode<#k,#v> {
   defines KVFactory<#k,#v>
   refines BalancedTreeNode<SearchTreeNode<#k,#v>,#k,#v>
+  refines Duplicate
 }
 
 define SearchTreeNode {
@@ -148,6 +153,18 @@
 
   newNode (k,v) {
     return #self{ 1, k, v, empty, empty }
+  }
+
+  duplicate () {
+    optional #self lower2 <- empty
+    if (present(lower)) {
+      lower2 <- require(lower).duplicate()
+    }
+    optional #self higher2 <- empty
+    if (present(higher)) {
+      higher2 <- require(higher).duplicate()
+    }
+    return #self{ height, key, value, lower2, higher2 }
   }
 
   getLower ()   { return lower }
diff --git a/lib/container/src/Extension_Vector.cpp b/lib/container/src/Extension_Vector.cpp
--- a/lib/container/src/Extension_Vector.cpp
+++ b/lib/container/src/Extension_Vector.cpp
@@ -75,10 +75,14 @@
   inline ExtType_Vector(Category_Vector& p, Params<1>::Type params) : Type_Vector(p, params) {}
 };
 
-struct VectorOrder : public AnonymousOrder {
+struct VectorOrder final : public AnonymousOrder {
   VectorOrder(BoxedValue container, const VectorType& v)
     : AnonymousOrder(container, Function_Order_next, Function_Order_get), values(v) {}
 
+  BoxedValue Var_self() final {
+    return VAR_SELF;
+  }
+
   BoxedValue Call_next(const BoxedValue& self) final {
     if (index+1 >= values.size()) {
       return Var_empty;
@@ -101,23 +105,28 @@
   inline ExtValue_Vector(S<Type_Vector> p, VectorType v)
     : Value_Vector(p), values(std::move(v)) {}
 
-  ReturnTuple Call_append(const BoxedValue& Var_self, const ParamTuple& params, const ValueTuple& args) final {
+  ReturnTuple Call_append(const ParamTuple& params, const ValueTuple& args) final {
     TRACE_FUNCTION("Vector.append")
     const BoxedValue& Var_arg1 = (args.At(0));
     values.push_back(Var_arg1);
-    return ReturnTuple(Var_self);
+    return ReturnTuple(VAR_SELF);
   }
 
-  ReturnTuple Call_defaultOrder(const BoxedValue& Var_self, const ParamTuple& params, const ValueTuple& args) final {
+  ReturnTuple Call_defaultOrder(const ParamTuple& params, const ValueTuple& args) final {
     TRACE_FUNCTION("Vector.defaultOrder")
     if (values.empty()) {
       return ReturnTuple(Var_empty);
     } else {
-      return ReturnTuple(BoxedValue(new VectorOrder(Var_self, values)));
+      return ReturnTuple(BoxedValue::New<VectorOrder>(VAR_SELF, values));
     }
   }
 
-  ReturnTuple Call_pop(const BoxedValue& Var_self, const ParamTuple& params, const ValueTuple& args) final {
+  ReturnTuple Call_duplicate(const ParamTuple& params, const ValueTuple& args) final {
+    TRACE_FUNCTION("Vector.duplicate")
+    return ReturnTuple(BoxedValue::New<ExtValue_Vector>(parent, values));
+  }
+
+  ReturnTuple Call_pop(const ParamTuple& params, const ValueTuple& args) final {
     TRACE_FUNCTION("Vector.pop")
     if (values.empty()) {
       BUILTIN_FAIL(Box_String(PrimString_FromLiteral("no elements left to pop")))
@@ -127,14 +136,14 @@
     return ReturnTuple(value);
   }
 
-  ReturnTuple Call_push(const BoxedValue& Var_self, const ParamTuple& params, const ValueTuple& args) final {
+  ReturnTuple Call_push(const ParamTuple& params, const ValueTuple& args) final {
     TRACE_FUNCTION("Vector.push")
     const BoxedValue& Var_arg1 = (args.At(0));
     values.push_back(Var_arg1);
-    return ReturnTuple(Var_self);
+    return ReturnTuple(VAR_SELF);
   }
 
-  ReturnTuple Call_readAt(const BoxedValue& Var_self, const ParamTuple& params, const ValueTuple& args) final {
+  ReturnTuple Call_readAt(const ParamTuple& params, const ValueTuple& args) final {
     TRACE_FUNCTION("Vector.readAt")
     const PrimInt Var_arg1 = (args.At(0)).AsInt();
     if (Var_arg1 < 0 || Var_arg1 >= values.size()) {
@@ -143,12 +152,12 @@
     return ReturnTuple(values[Var_arg1]);
   }
 
-  ReturnTuple Call_size(const BoxedValue& Var_self, const ParamTuple& params, const ValueTuple& args) final {
+  ReturnTuple Call_size(const ParamTuple& params, const ValueTuple& args) final {
     TRACE_FUNCTION("Vector.size")
     return ReturnTuple(Box_Int(values.size()));
   }
 
-  ReturnTuple Call_writeAt(const BoxedValue& Var_self, const ParamTuple& params, const ValueTuple& args) final {
+  ReturnTuple Call_writeAt(const ParamTuple& params, const ValueTuple& args) final {
     TRACE_FUNCTION("Vector.writeAt")
     const PrimInt Var_arg1 = (args.At(0)).AsInt();
     const BoxedValue& Var_arg2 = (args.At(1));
@@ -156,7 +165,7 @@
       FAIL() << "index " << Var_arg1 << " is out of bounds";
     }
     values[Var_arg1] = Var_arg2;
-    return ReturnTuple(Var_self);
+    return ReturnTuple(VAR_SELF);
   }
 
   // vector<#x>
@@ -167,14 +176,16 @@
   static auto& category = *new ExtCategory_Vector();
   return category;
 }
+
 S<Type_Vector> CreateType_Vector(Params<1>::Type params) {
   static auto& cache = *new InstanceCache<1, Type_Vector>([](Params<1>::Type params) {
       return S_get(new ExtType_Vector(CreateCategory_Vector(), params));
     });
   return cache.GetOrCreate(params);
 }
+
 BoxedValue CreateValue_Vector(S<Type_Vector> parent, VectorType values) {
-  return BoxedValue(new ExtValue_Vector(parent, values));
+  return BoxedValue::New<ExtValue_Vector>(parent, values);
 }
 
 #ifdef ZEOLITE_PUBLIC_NAMESPACE
diff --git a/lib/container/tree-set.0rp b/lib/container/tree-set.0rp
--- a/lib/container/tree-set.0rp
+++ b/lib/container/tree-set.0rp
@@ -19,6 +19,7 @@
 concrete TreeSet<#k> {
   refines Container
   refines DefaultOrder<#k>
+  refines Duplicate
   refines SetReader<#k>
   refines SetWriter<#k>
   #k defines LessThan<#k>
diff --git a/lib/container/tree-set.0rt b/lib/container/tree-set.0rt
--- a/lib/container/tree-set.0rt
+++ b/lib/container/tree-set.0rt
@@ -170,3 +170,17 @@
     fail("Failed")
   }
 }
+
+unittest duplicate {
+  TreeSet<Int> set <- TreeSet<Int>.new().add(1).add(2)
+
+  TreeSet<Int> copy <- set.duplicate()
+  \ Testing.checkEquals<?>(copy.size(),2)
+  \ Testing.checkEquals<?>(copy.member(1),true)
+  \ Testing.checkEquals<?>(copy.member(1),true)
+
+  \ copy.remove(2)
+  \ Testing.checkEquals<?>(copy.size(),1)
+  \ Testing.checkEquals<?>(copy.member(1),true)
+  \ Testing.checkEquals<?>(set.member(2),true)
+}
diff --git a/lib/container/tree-set.0rx b/lib/container/tree-set.0rx
--- a/lib/container/tree-set.0rx
+++ b/lib/container/tree-set.0rx
@@ -27,6 +27,10 @@
     return tree.size()
   }
 
+  duplicate () {
+    return #self{ tree.duplicate() }
+  }
+
   add (k) {
     \ tree.set(k,Void.void())
     return self
diff --git a/lib/container/type-map.0rp b/lib/container/type-map.0rp
--- a/lib/container/type-map.0rp
+++ b/lib/container/type-map.0rp
@@ -18,6 +18,8 @@
 
 // A map that stores values of arbitrary types.
 concrete TypeMap {
+  refines Duplicate
+
   // Create a new map.
   @type new () -> (TypeMap)
 
diff --git a/lib/container/type-map.0rt b/lib/container/type-map.0rt
--- a/lib/container/type-map.0rt
+++ b/lib/container/type-map.0rt
@@ -57,6 +57,22 @@
   \ Testing.checkOptional<?>(tree.get<String>(keyString),"a")
 }
 
+unittest duplicate {
+  TypeKey<Int>    keyInt    <- TypeKey<Int>.new()
+  TypeKey<String> keyString <- TypeKey<String>.new()
+
+  TypeMap tree <- TypeMap.new().set<?>(keyInt,1).set<?>(keyString,"a")
+
+  TypeMap copy <- tree.duplicate()
+  \ Testing.checkOptional<?>(copy.get<?>(keyInt),1)
+  \ Testing.checkOptional<?>(copy.get<?>(keyString),"a")
+
+  \ copy.remove(keyString)
+  \ Testing.checkOptional<?>(copy.get<?>(keyInt),1)
+  \ Testing.checkOptional<?>(copy.get<?>(keyString),empty)
+  \ Testing.checkOptional<?>(tree.get<?>(keyString),"a")
+}
+
 concrete Value {
   refines Formatted
   defines Equals<Value>
diff --git a/lib/container/type-map.0rx b/lib/container/type-map.0rx
--- a/lib/container/type-map.0rx
+++ b/lib/container/type-map.0rx
@@ -17,14 +17,18 @@
 // Author: Kevin P. Barry [ta0kira@gmail.com]
 
 define TypeMap {
-  @value SearchTree<TypeKey<any>,LockedType<any>> tree
+  @value SearchTree<TypeKey<any>,GenericValue<any>> tree
 
   new () {
-    return TypeMap{ SearchTree<TypeKey<any>,LockedType<any>>.new() }
+    return TypeMap{ SearchTree<TypeKey<any>,GenericValue<any>>.new() }
   }
 
+  duplicate () {
+    return #self{ tree.duplicate() }
+  }
+
   set (k,v) {
-    \ tree.set(k,LockedType:create<?>(k,v))
+    \ tree.set(k,GenericValue:create<?>(v))
     return self
   }
 
@@ -35,9 +39,9 @@
 
   get (k) {
     scoped {
-      optional LockedType<any> value <- tree.get(k)
+      optional GenericValue<any> value <- tree.get(k)
     } in if (present(value)) {
-      return require(value).check<?>(k)
+      return require(value).check<#x>()
     } else {
       return empty
     }
@@ -47,15 +51,15 @@
 define TypeKey {
   $ReadOnly[counterMutex,index]$
 
-  @category Mutex counterMutex <- SimpleMutex.new()
+  @category Mutex counterMutex <- SpinlockMutex.new()
   @category Int   counter      <- 0
   @value Int index
 
   new () {
     scoped {
-      MutexLock lock <- MutexLock.lock(counterMutex)
+      \ counterMutex.lock()
     } cleanup {
-      \ lock.freeResource()
+      \ counterMutex.unlock()
     } in return TypeKey<#x>{ (counter <- counter+1) }
   }
 
@@ -82,24 +86,21 @@
   }
 }
 
-concrete LockedType<|#x> {
-  @category create<#x> (TypeKey<#x>,#x) -> (LockedType<#x>)
-  @value check<#y> (TypeKey<#y>) -> (optional #y)
+concrete GenericValue<|#x> {
+  @category create<#x> (#x) -> (GenericValue<#x>)
+  @value check<#y> () -> (optional #y)
 }
 
-define LockedType {
-  @value TypeKey<#x> key
+define GenericValue {
+  $ReadOnly[value]$
+
   @value #x value
 
-  create (k,v) {
-    return LockedType<#x>{ k, v }
+  create (v) {
+    return GenericValue<#x>{ v }
   }
 
-  check (k) {
-    if (key `TypeKey<any>.equals` k) {
-      return reduce<#x,#y>(value)
-    } else {
-      return empty
-    }
+  check () {
+    return reduce<#x,#y>(value)
   }
 }
diff --git a/lib/container/vector.0rp b/lib/container/vector.0rp
--- a/lib/container/vector.0rp
+++ b/lib/container/vector.0rp
@@ -23,6 +23,7 @@
 concrete Vector<#x> {
   refines Append<#x>
   refines DefaultOrder<#x>
+  refines Duplicate
   refines Stack<#x>
   refines ReadAt<#x>
   refines WriteAt<#x>
diff --git a/lib/container/vector.0rt b/lib/container/vector.0rt
--- a/lib/container/vector.0rt
+++ b/lib/container/vector.0rt
@@ -131,6 +131,20 @@
   \ Testing.checkEquals<?>(index,6)
 }
 
+unittest duplicate {
+  Vector<Int> vector <- Vector:create<Int>().push(1).push(2)
+
+  Vector<Int> copy <- vector.duplicate()
+  \ Testing.checkEquals<?>(copy.size(),2)
+  \ Testing.checkEquals<?>(copy.readAt(0),1)
+  \ Testing.checkEquals<?>(copy.readAt(1),2)
+
+  \ copy.pop()
+  \ Testing.checkEquals<?>(copy.size(),1)
+  \ Testing.checkEquals<?>(copy.readAt(0),1)
+  \ Testing.checkEquals<?>(vector.readAt(1),2)
+}
+
 
 testcase "distinct default values in pre-sized Vector" {
   success
diff --git a/lib/file/src/Extension_RawFileReader.cpp b/lib/file/src/Extension_RawFileReader.cpp
--- a/lib/file/src/Extension_RawFileReader.cpp
+++ b/lib/file/src/Extension_RawFileReader.cpp
@@ -45,7 +45,7 @@
       filename(args.At(0).AsString()),
       file(new std::ifstream(filename, std::ios::in | std::ios::binary)) {}
 
-  ReturnTuple Call_freeResource(const BoxedValue& Var_self, const ParamTuple& params, const ValueTuple& args) final {
+  ReturnTuple Call_freeResource(const ParamTuple& params, const ValueTuple& args) final {
     TRACE_FUNCTION("RawFileReader.freeResource")
     std::lock_guard<std::mutex> lock(mutex);
     if (file) {
@@ -54,7 +54,7 @@
     return ReturnTuple();
   }
 
-  ReturnTuple Call_getFileError(const BoxedValue& Var_self, const ParamTuple& params, const ValueTuple& args) final {
+  ReturnTuple Call_getFileError(const ParamTuple& params, const ValueTuple& args) final {
     TRACE_FUNCTION("RawFileReader.getFileError")
     std::lock_guard<std::mutex> lock(mutex);
     if (!file) {
@@ -69,13 +69,13 @@
     return ReturnTuple(Var_empty);
   }
 
-  ReturnTuple Call_pastEnd(const BoxedValue& Var_self, const ParamTuple& params, const ValueTuple& args) final {
+  ReturnTuple Call_pastEnd(const ParamTuple& params, const ValueTuple& args) final {
     TRACE_FUNCTION("RawFileReader.pastEnd")
     std::lock_guard<std::mutex> lock(mutex);
     return ReturnTuple(Box_Bool(!file || file->fail() || file->eof()));
   }
 
-  ReturnTuple Call_readBlock(const BoxedValue& Var_self, const ParamTuple& params, const ValueTuple& args) final {
+  ReturnTuple Call_readBlock(const ParamTuple& params, const ValueTuple& args) final {
     TRACE_FUNCTION("RawFileReader.readBlock")
     TRACE_CREATION
     std::lock_guard<std::mutex> lock(mutex);
@@ -114,12 +114,14 @@
   static auto& category = *new ExtCategory_RawFileReader();
   return category;
 }
+
 S<Type_RawFileReader> CreateType_RawFileReader(Params<0>::Type params) {
   static const auto cached = S_get(new ExtType_RawFileReader(CreateCategory_RawFileReader(), Params<0>::Type()));
   return cached;
 }
+
 BoxedValue CreateValue_RawFileReader(S<Type_RawFileReader> parent, const ValueTuple& args) {
-  return BoxedValue(new ExtValue_RawFileReader(parent, args));
+  return BoxedValue::New<ExtValue_RawFileReader>(parent, args);
 }
 
 #ifdef ZEOLITE_PUBLIC_NAMESPACE
diff --git a/lib/file/src/Extension_RawFileWriter.cpp b/lib/file/src/Extension_RawFileWriter.cpp
--- a/lib/file/src/Extension_RawFileWriter.cpp
+++ b/lib/file/src/Extension_RawFileWriter.cpp
@@ -45,7 +45,7 @@
       filename(args.At(0).AsString()),
       file(new std::ofstream(filename, std::ios::out | std::ios::binary | std::ios::trunc | std::ios::ate)) {}
 
-  ReturnTuple Call_freeResource(const BoxedValue& Var_self, const ParamTuple& params, const ValueTuple& args) final {
+  ReturnTuple Call_freeResource(const ParamTuple& params, const ValueTuple& args) final {
     TRACE_FUNCTION("RawFileWriter.freeResource")
     std::lock_guard<std::mutex> lock(mutex);
     if (file) {
@@ -54,7 +54,7 @@
     return ReturnTuple();
   }
 
-  ReturnTuple Call_getFileError(const BoxedValue& Var_self, const ParamTuple& params, const ValueTuple& args) final {
+  ReturnTuple Call_getFileError(const ParamTuple& params, const ValueTuple& args) final {
     TRACE_FUNCTION("RawFileWriter.getFileError")
     std::lock_guard<std::mutex> lock(mutex);
     if (!file) {
@@ -69,7 +69,7 @@
     return ReturnTuple(Var_empty);
   }
 
-  ReturnTuple Call_writeBlock(const BoxedValue& Var_self, const ParamTuple& params, const ValueTuple& args) final {
+  ReturnTuple Call_writeBlock(const ParamTuple& params, const ValueTuple& args) final {
     TRACE_FUNCTION("RawFileWriter.writeBlock")
     TRACE_CREATION
     std::lock_guard<std::mutex> lock(mutex);
@@ -101,7 +101,7 @@
   return cached;
 }
 BoxedValue CreateValue_RawFileWriter(S<Type_RawFileWriter> parent, const ValueTuple& args) {
-  return BoxedValue(new ExtValue_RawFileWriter(parent, args));
+  return BoxedValue::New<ExtValue_RawFileWriter>(parent, args);
 }
 
 #ifdef ZEOLITE_PUBLIC_NAMESPACE
diff --git a/lib/math/.zeolite-module b/lib/math/.zeolite-module
--- a/lib/math/.zeolite-module
+++ b/lib/math/.zeolite-module
@@ -1,9 +1,11 @@
 root: "../.."
 path: "lib/math"
+public_deps: [
+  "lib/util"
+]
 private_deps: [
   "lib/container"
   "lib/testing"
-  "lib/util"
 ]
 extra_files: [
   category_source {
diff --git a/lib/math/categorical-tree.0rp b/lib/math/categorical-tree.0rp
--- a/lib/math/categorical-tree.0rp
+++ b/lib/math/categorical-tree.0rp
@@ -32,6 +32,7 @@
 //   on the number of distinct #c values with non-zero weights.
 concrete CategoricalTree<#c> {
   refines ReadAt<#c>
+  refines Duplicate
   #c defines LessThan<#c>
 
   // Create a new distribution.
@@ -52,6 +53,16 @@
   // Notes:
   // - If the category isn't in the tree, its weight is 0.
   @value getWeight (#c) -> (Int)
+
+  // Increments the weight of the value by 1.
+  @value incrWeight (#c) -> (#self)
+
+  // Decrements the weight of the value by 1.
+  //
+  // Notes:
+  // - Do not call this for values that already have 0 weight. This is primarily
+  //   meant for use when locate is used to choose a value to decrement.
+  @value decrWeight (#c) -> (#self)
 
   // Return the value at the given offset.
   //
diff --git a/lib/math/categorical-tree.0rt b/lib/math/categorical-tree.0rt
--- a/lib/math/categorical-tree.0rt
+++ b/lib/math/categorical-tree.0rt
@@ -46,7 +46,7 @@
   \ Testing.checkEquals<?>(tree.getWeight(6),2)
 
   traverse (Counter.zeroIndexed(tree.getTotal()) -> Int pos) {
-    \ Testing.checkOptional<?>(tree.locate(pos),expected.readAt(pos))
+    \ Testing.checkEquals<?>(tree.locate(pos),expected.readAt(pos))
   }
 }
 
@@ -81,13 +81,48 @@
   \ Testing.checkEquals<?>(tree.getWeight(7),3)
 
   traverse (Counter.zeroIndexed(tree.getTotal()) -> Int pos) {
-    \ Testing.checkOptional<?>(tree.locate(pos),expected.readAt(pos))
+    \ Testing.checkEquals<?>(tree.locate(pos),expected.readAt(pos))
   }
 
   // Use ReadAt aliases for the same functionality.
   traverse (Counter.zeroIndexed(tree.size()) -> Int pos) {
-    \ Testing.checkOptional<?>(tree.readAt(pos),expected.readAt(pos))
+    \ Testing.checkEquals<?>(tree.readAt(pos),expected.readAt(pos))
   }
+}
+
+unittest increment {
+  CategoricalTree<Int> tree <- CategoricalTree<Int>.new()
+    .incrWeight(1)
+    .incrWeight(1)
+    .incrWeight(1)
+
+  \ Testing.checkEquals<?>(tree.getTotal(),3)
+  \ Testing.checkEquals<?>(tree.getWeight(1),3)
+}
+
+unittest decrement {
+  CategoricalTree<Int> tree <- CategoricalTree<Int>.new()
+    .setWeight(1,10)
+    .decrWeight(1)
+    .decrWeight(1)
+    .decrWeight(1)
+
+  \ Testing.checkEquals<?>(tree.getTotal(),7)
+  \ Testing.checkEquals<?>(tree.getWeight(1),7)
+}
+
+unittest duplicate {
+  CategoricalTree<Int> tree <- CategoricalTree<Int>.new().setWeight(1,1).setWeight(2,2)
+
+  CategoricalTree<Int> copy <- tree.duplicate()
+  \ Testing.checkEquals<?>(copy.size(),3)
+  \ Testing.checkEquals<?>(copy.getWeight(1),1)
+  \ Testing.checkEquals<?>(copy.getWeight(2),2)
+
+  \ copy.setWeight(2,0)
+  \ Testing.checkEquals<?>(copy.size(),1)
+  \ Testing.checkEquals<?>(copy.getWeight(1),1)
+  \ Testing.checkEquals<?>(tree.getWeight(2),2)
 }
 
 unittest integrationTest {
diff --git a/lib/math/categorical-tree.0rx b/lib/math/categorical-tree.0rx
--- a/lib/math/categorical-tree.0rx
+++ b/lib/math/categorical-tree.0rx
@@ -23,6 +23,10 @@
     return #self{ AutoBinaryTree<CategoricalTreeNode<#c>,#c,Int,CategoricalSearch<#c>>.new() }
   }
 
+  duplicate () {
+    return #self{ tree.duplicate() }
+  }
+
   getTotal () {
     return CategoricalTreeNode<#c>.tryTotal(tree.getRoot())
   }
@@ -48,6 +52,16 @@
     }
   }
 
+  incrWeight (cat) {
+    \ setWeight(cat,getWeight(cat)+1)
+    return self
+  }
+
+  decrWeight (cat) {
+    \ setWeight(cat,getWeight(cat)-1)
+    return self
+  }
+
   locate (pos) {
     if (pos < 0) {
       fail("position must not be negative")
@@ -145,6 +159,7 @@
   defines KVFactory<#c,Int>
   refines CategoricalSearch<#c>
   refines BalancedTreeNode<CategoricalTreeNode<#c>,#c,Int>
+  refines Duplicate
 
   @type findPosition (Int,optional CategoricalSearch<#c>) -> (optional #c)
   @type tryTotal (optional CategoricalSearch<#c>) -> (Int)
@@ -161,6 +176,18 @@
   @value Int total
   @value optional #self lower
   @value optional #self higher
+
+  duplicate () {
+    optional #self lower2 <- empty
+    if (present(lower)) {
+      lower2 <- require(lower).duplicate()
+    }
+    optional #self higher2 <- empty
+    if (present(higher)) {
+      higher2 <- require(higher).duplicate()
+    }
+    return #self{ height, key, size, total, lower2, higher2 }
+  }
 
   findPosition (pos,node) (cat) {
     cat <- empty
diff --git a/lib/math/random.0rp b/lib/math/random.0rp
--- a/lib/math/random.0rp
+++ b/lib/math/random.0rp
@@ -26,7 +26,10 @@
   refines Generator<Float>
 
   // Creates a new generator with the specified lambda value.
-  @type new (Float) -> (Generator<Float>)
+  @type new (Float) -> (#self)
+
+  // Resets the seed for RNG.
+  @value setSeed (Int) -> (#self)
 }
 
 // Generates Gaussian-distributed Float values.
@@ -34,7 +37,10 @@
   refines Generator<Float>
 
   // Creates a new generator with the specified mean and standard deviation.
-  @type new (Float,Float) -> (Generator<Float>)
+  @type new (Float,Float) -> (#self)
+
+  // Resets the seed for RNG.
+  @value setSeed (Int) -> (#self)
 }
 
 // Generates uniformly-distributed Float values.
@@ -42,5 +48,19 @@
   refines Generator<Float>
 
   // Creates a new generator with the specified min and max values.
-  @type new (Float,Float) -> (Generator<Float>)
+  @type new (Float,Float) -> (#self)
+
+  // Resets the seed for RNG.
+  @value setSeed (Int) -> (#self)
+}
+
+concrete Randomize {
+  // Creates a permutation from the CategoricalTree.
+  //
+  // 1. Creates a copy of the CategoricalTree.
+  // 2. Samples from the copy *without* replacement until the tree is empty.
+  //
+  // Notes:
+  // - The Generator must only return values in [0,1).
+  @category permuteFrom<#c> (CategoricalTree<#c>,Generator<Float>,Append<#c>) -> ()
 }
diff --git a/lib/math/random.0rt b/lib/math/random.0rt
--- a/lib/math/random.0rt
+++ b/lib/math/random.0rt
@@ -123,3 +123,103 @@
 
   \ Testing.checkBetween<?>(sum/count.asFloat(),min-0.1*(max-min),max-0.1*(max-min))
 }
+
+
+testcase "distribution seed checks" {
+  success
+}
+
+unittest exponential {
+  Int count <- 10000
+  Float lambda <- 10.0
+  $ReadOnly[count,lambda]$
+
+  RandomExponential random1 <- RandomExponential.new(lambda)
+  RandomExponential random2 <- RandomExponential.new(lambda)
+
+  Float v1 <- random1.generate()
+  Float v2 <- random2.generate()
+  random1 <- random1.setSeed(123)
+  Float v3 <- random1.generate()
+  random2 <- random2.setSeed(123)
+  Float v4 <- random2.generate()
+
+  \ Testing.checkEquals<?>(v1 == v2,false)
+  \ Testing.checkEquals<?>(v1 == v3,false)
+  \ Testing.checkEquals<?>(v3,v4)
+}
+
+unittest gaussian {
+  Int count <- 10000
+  Float mean <- 100.0
+  Float sd <- 1.0
+  $ReadOnly[count,mean,sd]$
+
+  RandomGaussian random1 <- RandomGaussian.new(mean,sd)
+  RandomGaussian random2 <- RandomGaussian.new(mean,sd)
+
+  Float v1 <- random1.generate()
+  Float v2 <- random2.generate()
+  random1 <- random1.setSeed(123)
+  Float v3 <- random1.generate()
+  random2 <- random2.setSeed(123)
+  Float v4 <- random2.generate()
+
+  \ Testing.checkEquals<?>(v1 == v2,false)
+  \ Testing.checkEquals<?>(v1 == v3,false)
+  \ Testing.checkEquals<?>(v3,v4)
+}
+
+unittest uniform {
+  Int count <- 10000
+  Float min <- -11.0
+  Float max <- 5.0
+  $ReadOnly[count,min,max]$
+
+  RandomUniform random1 <- RandomUniform.new(min,max)
+  RandomUniform random2 <- RandomUniform.new(min,max)
+
+  Float v1 <- random1.generate()
+  Float v2 <- random2.generate()
+  random1 <- random1.setSeed(123)
+  Float v3 <- random1.generate()
+  random2 <- random2.setSeed(123)
+  Float v4 <- random2.generate()
+
+  \ Testing.checkEquals<?>(v1 == v2,false)
+  \ Testing.checkEquals<?>(v1 == v3,false)
+  \ Testing.checkEquals<?>(v3,v4)
+}
+
+
+testcase "Randomize tests" {
+  success
+}
+
+unittest permuteFrom {
+  CategoricalTree<String> tree <- CategoricalTree<String>.new()
+      .setWeight("a",2)
+      .setWeight("b",3)
+      .setWeight("c",1)
+
+  Vector<String> expected <- Vector:create<String>()
+      .append("a")
+      .append("a")
+      .append("b")
+      .append("b")
+      .append("b")
+      .append("c")
+
+  Vector<String> output <- Vector:create<String>()
+  \ Randomize:permuteFrom<?>(tree,RandomUniform.new(0.0,1.0),output)
+  \ Sorting:sort<?>(output)
+
+  \ Testing.checkEquals<?>(output.size(),expected.size())
+  traverse (Counter.zeroIndexed(output.size()) -> Int pos) {
+    \ Testing.checkEquals<?>(output.readAt(pos),expected.readAt(pos))
+  }
+
+  \ Testing.checkEquals<?>(tree.getWeight("a"),2)
+  \ Testing.checkEquals<?>(tree.getWeight("b"),3)
+  \ Testing.checkEquals<?>(tree.getWeight("c"),1)
+}
diff --git a/lib/math/random.0rx b/lib/math/random.0rx
new file mode 100644
--- /dev/null
+++ b/lib/math/random.0rx
@@ -0,0 +1,29 @@
+/* -----------------------------------------------------------------------------
+Copyright 2021 Kevin P. Barry
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+----------------------------------------------------------------------------- */
+
+// Author: Kevin P. Barry [ta0kira@gmail.com]
+
+define Randomize {
+  permuteFrom (tree,random,output) {
+    CategoricalTree<#c> copy <- tree.duplicate()
+    $Hidden[tree]$
+    while (copy.getTotal() > 0) {
+      #c cat <- copy.locate((random.generate()*copy.getTotal().asFloat()).asInt())
+      \ output.append(cat)
+      \ copy.decrWeight(cat)
+    }
+  }
+}
diff --git a/lib/math/src/Extension_Math.cpp b/lib/math/src/Extension_Math.cpp
--- a/lib/math/src/Extension_Math.cpp
+++ b/lib/math/src/Extension_Math.cpp
@@ -202,6 +202,7 @@
   static auto& category = *new ExtCategory_Math();
   return category;
 }
+
 S<Type_Math> CreateType_Math(Params<0>::Type params) {
   static const auto cached = S_get(new ExtType_Math(CreateCategory_Math(), Params<0>::Type()));
   return cached;
diff --git a/lib/math/src/Extension_RandomExponential.cpp b/lib/math/src/Extension_RandomExponential.cpp
--- a/lib/math/src/Extension_RandomExponential.cpp
+++ b/lib/math/src/Extension_RandomExponential.cpp
@@ -50,13 +50,21 @@
 struct ExtValue_RandomExponential : public Value_RandomExponential {
   inline ExtValue_RandomExponential(S<Type_RandomExponential> p, const ValueTuple& args)
     : Value_RandomExponential(p),
+      generator_((int) (long) this),
       distribution_(args.At(0).AsFloat()) {}
 
-  ReturnTuple Call_generate(const BoxedValue& Var_self, const ParamTuple& params, const ValueTuple& args) final {
+  ReturnTuple Call_generate(const ParamTuple& params, const ValueTuple& args) final {
     TRACE_FUNCTION("RandomExponential.generate")
     return ReturnTuple(Box_Float(distribution_(generator_)));
   }
 
+  ReturnTuple Call_setSeed(const ParamTuple& params, const ValueTuple& args) final {
+    TRACE_FUNCTION("RandomExponential.setSeed")
+    distribution_.reset();
+    generator_.seed(args.At(0).AsInt());
+    return ReturnTuple(VAR_SELF);
+  }
+
   std::default_random_engine generator_;
   std::exponential_distribution<double> distribution_;
 };
@@ -72,7 +80,7 @@
 }
 
 BoxedValue CreateValue_RandomExponential(S<Type_RandomExponential> parent, const ValueTuple& args) {
-  return BoxedValue(new ExtValue_RandomExponential(parent, args));
+  return BoxedValue::New<ExtValue_RandomExponential>(parent, args);
 }
 
 #ifdef ZEOLITE_PUBLIC_NAMESPACE
diff --git a/lib/math/src/Extension_RandomGaussian.cpp b/lib/math/src/Extension_RandomGaussian.cpp
--- a/lib/math/src/Extension_RandomGaussian.cpp
+++ b/lib/math/src/Extension_RandomGaussian.cpp
@@ -51,13 +51,21 @@
 struct ExtValue_RandomGaussian : public Value_RandomGaussian {
   inline ExtValue_RandomGaussian(S<Type_RandomGaussian> p, const ValueTuple& args)
     : Value_RandomGaussian(p),
+      generator_((int) (long) this),
       distribution_(args.At(0).AsFloat(), args.At(1).AsFloat()) {}
 
-  ReturnTuple Call_generate(const BoxedValue& Var_self, const ParamTuple& params, const ValueTuple& args) final {
+  ReturnTuple Call_generate(const ParamTuple& params, const ValueTuple& args) final {
     TRACE_FUNCTION("RandomGaussian.generate")
     return ReturnTuple(Box_Float(distribution_(generator_)));
   }
 
+  ReturnTuple Call_setSeed(const ParamTuple& params, const ValueTuple& args) final {
+    TRACE_FUNCTION("RandomGaussian.setSeed")
+    distribution_.reset();
+    generator_.seed(args.At(0).AsInt());
+    return ReturnTuple(VAR_SELF);
+  }
+
   std::default_random_engine generator_;
   std::normal_distribution<double> distribution_;
 };
@@ -73,7 +81,7 @@
 }
 
 BoxedValue CreateValue_RandomGaussian(S<Type_RandomGaussian> parent, const ValueTuple& args) {
-  return BoxedValue(new ExtValue_RandomGaussian(parent, args));
+  return BoxedValue::New<ExtValue_RandomGaussian>(parent, args);
 }
 
 #ifdef ZEOLITE_PUBLIC_NAMESPACE
diff --git a/lib/math/src/Extension_RandomUniform.cpp b/lib/math/src/Extension_RandomUniform.cpp
--- a/lib/math/src/Extension_RandomUniform.cpp
+++ b/lib/math/src/Extension_RandomUniform.cpp
@@ -50,13 +50,21 @@
 struct ExtValue_RandomUniform : public Value_RandomUniform {
   inline ExtValue_RandomUniform(S<Type_RandomUniform> p, const ValueTuple& args)
     : Value_RandomUniform(p),
+      generator_((int) (long) this),
       distribution_(args.At(0).AsFloat(), args.At(1).AsFloat()) {}
 
-  ReturnTuple Call_generate(const BoxedValue& Var_self, const ParamTuple& params, const ValueTuple& args) final {
+  ReturnTuple Call_generate(const ParamTuple& params, const ValueTuple& args) final {
     TRACE_FUNCTION("RandomUniform.generate")
     return ReturnTuple(Box_Float(distribution_(generator_)));
   }
 
+  ReturnTuple Call_setSeed(const ParamTuple& params, const ValueTuple& args) final {
+    TRACE_FUNCTION("RandomUniform.setSeed")
+    distribution_.reset();
+    generator_.seed(args.At(0).AsInt());
+    return ReturnTuple(VAR_SELF);
+  }
+
   std::default_random_engine generator_;
   std::uniform_real_distribution<double> distribution_;
 };
@@ -72,7 +80,7 @@
 }
 
 BoxedValue CreateValue_RandomUniform(S<Type_RandomUniform> parent, const ValueTuple& args) {
-  return BoxedValue(new ExtValue_RandomUniform(parent, args));
+  return BoxedValue::New<ExtValue_RandomUniform>(parent, args);
 }
 
 #ifdef ZEOLITE_PUBLIC_NAMESPACE
diff --git a/lib/thread/src/Extension_Enumerated.cpp b/lib/thread/src/Extension_Enumerated.cpp
--- a/lib/thread/src/Extension_Enumerated.cpp
+++ b/lib/thread/src/Extension_Enumerated.cpp
@@ -115,10 +115,10 @@
   inline ExtValue_EnumeratedWait(S<Type_EnumeratedWait> p, S<Barrier> b, int i)
     : Value_EnumeratedWait(p), barrier(b), index(i) {}
 
-  ReturnTuple Call_wait(const BoxedValue& Var_self, const ParamTuple& params, const ValueTuple& args) final {
+  ReturnTuple Call_wait(const ParamTuple& params, const ValueTuple& args) final {
     TRACE_FUNCTION("EnumeratedWait.wait")
     barrier->Wait(index);
-    return ReturnTuple(Var_self);
+    return ReturnTuple(VAR_SELF);
   }
 
   ~ExtValue_EnumeratedWait() {
@@ -139,7 +139,7 @@
 }
 BoxedValue CreateValue_EnumeratedWait(
   S<Type_EnumeratedWait> parent, S<Barrier> b, int i) {
-  return BoxedValue(new ExtValue_EnumeratedWait(parent, b, i));
+  return BoxedValue::New<ExtValue_EnumeratedWait>(parent, b, i);
 }
 
 #ifdef ZEOLITE_PRIVATE_NAMESPACE
@@ -182,7 +182,7 @@
   inline ExtValue_EnumeratedBarrier(S<Type_EnumeratedBarrier> p, std::vector<BoxedValue> w)
     : Value_EnumeratedBarrier(p), waits(std::move(w)) {}
 
-  ReturnTuple Call_readAt(const BoxedValue& Var_self, const ParamTuple& params, const ValueTuple& args) final {
+  ReturnTuple Call_readAt(const ParamTuple& params, const ValueTuple& args) final {
     TRACE_FUNCTION("EnumeratedBarrier.readAt")
     const PrimInt Var_arg1 = (args.At(0)).AsInt();
     if (Var_arg1 < 0 || Var_arg1 >= waits.size()) {
@@ -191,7 +191,7 @@
     return ReturnTuple(waits[Var_arg1]);
   }
 
-  ReturnTuple Call_size(const BoxedValue& Var_self, const ParamTuple& params, const ValueTuple& args) final {
+  ReturnTuple Call_size(const ParamTuple& params, const ValueTuple& args) final {
     TRACE_FUNCTION("EnumeratedBarrier.size")
     return ReturnTuple(Box_Int(waits.size()));
   }
@@ -204,13 +204,15 @@
   static auto& category = *new ExtCategory_EnumeratedBarrier();
   return category;
 }
+
 S<Type_EnumeratedBarrier> CreateType_EnumeratedBarrier(Params<0>::Type params) {
   static const auto cached = S_get(new ExtType_EnumeratedBarrier(CreateCategory_EnumeratedBarrier(), Params<0>::Type()));
   return cached;
 }
+
 BoxedValue CreateValue_EnumeratedBarrier(
   S<Type_EnumeratedBarrier> parent, std::vector<BoxedValue> w) {
-  return BoxedValue(new ExtValue_EnumeratedBarrier(parent, std::move(w)));
+  return BoxedValue::New<ExtValue_EnumeratedBarrier>(parent, std::move(w));
 }
 
 #ifdef ZEOLITE_PUBLIC_NAMESPACE
diff --git a/lib/thread/src/Extension_ProcessThread.cpp b/lib/thread/src/Extension_ProcessThread.cpp
--- a/lib/thread/src/Extension_ProcessThread.cpp
+++ b/lib/thread/src/Extension_ProcessThread.cpp
@@ -49,7 +49,7 @@
   inline ExtValue_ProcessThread(S<Type_ProcessThread> p, const ValueTuple& args)
     : Value_ProcessThread(p), routine(args.Only()) {}
 
-  ReturnTuple Call_detach(const BoxedValue& Var_self, const ParamTuple& params, const ValueTuple& args) final {
+  ReturnTuple Call_detach(const ParamTuple& params, const ValueTuple& args) final {
     TRACE_FUNCTION("ProcessThread.detach")
     S<std::thread> temp = thread;
     thread = nullptr;
@@ -58,15 +58,15 @@
     } else {
       temp->detach();
     }
-    return ReturnTuple(Var_self);
+    return ReturnTuple(VAR_SELF);
   }
 
-  ReturnTuple Call_isRunning(const BoxedValue& Var_self, const ParamTuple& params, const ValueTuple& args) final {
+  ReturnTuple Call_isRunning(const ParamTuple& params, const ValueTuple& args) final {
     TRACE_FUNCTION("ProcessThread.isRunning")
     return ReturnTuple(Box_Bool(isJoinable(thread.get())));
   }
 
-  ReturnTuple Call_join(const BoxedValue& Var_self, const ParamTuple& params, const ValueTuple& args) final {
+  ReturnTuple Call_join(const ParamTuple& params, const ValueTuple& args) final {
     TRACE_FUNCTION("ProcessThread.join")
     S<std::thread> temp = thread;
     thread = nullptr;
@@ -75,24 +75,25 @@
     } else {
       temp->join();
     }
-    return ReturnTuple(Var_self);
+    return ReturnTuple(VAR_SELF);
   }
 
-  ReturnTuple Call_start(const BoxedValue& Var_self, const ParamTuple& params, const ValueTuple& args) final {
+  ReturnTuple Call_start(const ParamTuple& params, const ValueTuple& args) final {
     TRACE_FUNCTION("ProcessThread.start")
     if (isJoinable(thread.get())) {
       FAIL() << "thread is already running";
     } else {
-      // NOTE: Capture Var_self so that the thread retains a reference while
+      // NOTE: Capture VAR_SELF so that the thread retains a reference while
       // it's still running. This allows the caller to hold a weak reference to
       // the thread.
+      const BoxedValue self = VAR_SELF;
       thread.reset(new std::thread(
-        capture_thread::ThreadCrosser::WrapCall([this,Var_self] {
+        capture_thread::ThreadCrosser::WrapCall([this,self] {
           TRACE_CREATION
           TypeValue::Call(routine, Function_Routine_run, ParamTuple(), ArgTuple());
         })));
     }
-    return ReturnTuple(Var_self);
+    return ReturnTuple(VAR_SELF);
   }
 
   inline static bool isJoinable(std::thread* thread) {
@@ -103,7 +104,7 @@
     S<std::thread> temp = thread;
     thread = nullptr;
     if (isJoinable(temp.get())) {
-      thread->detach();
+      temp->detach();
     }
   }
 
@@ -117,12 +118,14 @@
   static auto& category = *new ExtCategory_ProcessThread();
   return category;
 }
+
 S<Type_ProcessThread> CreateType_ProcessThread(Params<0>::Type params) {
   static const auto cached = S_get(new ExtType_ProcessThread(CreateCategory_ProcessThread(), Params<0>::Type()));
   return cached;
 }
+
 BoxedValue CreateValue_ProcessThread(S<Type_ProcessThread> parent, const ValueTuple& args) {
-  return BoxedValue(new ExtValue_ProcessThread(parent, args));
+  return BoxedValue::New<ExtValue_ProcessThread>(parent, args);
 }
 
 #ifdef ZEOLITE_PUBLIC_NAMESPACE
diff --git a/lib/thread/src/Extension_Realtime.cpp b/lib/thread/src/Extension_Realtime.cpp
--- a/lib/thread/src/Extension_Realtime.cpp
+++ b/lib/thread/src/Extension_Realtime.cpp
@@ -92,6 +92,7 @@
   static auto& category = *new ExtCategory_Realtime();
   return category;
 }
+
 S<Type_Realtime> CreateType_Realtime(Params<0>::Type params) {
   static const auto cached = S_get(new ExtType_Realtime(CreateCategory_Realtime(), Params<0>::Type()));
   return cached;
diff --git a/lib/thread/src/Extension_ThreadCondition.cpp b/lib/thread/src/Extension_ThreadCondition.cpp
--- a/lib/thread/src/Extension_ThreadCondition.cpp
+++ b/lib/thread/src/Extension_ThreadCondition.cpp
@@ -56,34 +56,34 @@
     pthread_mutex_init(&mutex, &mutex_attr);
   }
 
-  ReturnTuple Call_lock(const BoxedValue& Var_self, const ParamTuple& params, const ValueTuple& args) final {
+  ReturnTuple Call_lock(const ParamTuple& params, const ValueTuple& args) final {
     TRACE_FUNCTION("ThreadCondition.lock")
     int error = 0;
     if ((error = pthread_mutex_lock(&mutex)) != 0) {
       FailError("Error locking mutex", error);
     }
-    return ReturnTuple(Var_self);
+    return ReturnTuple(VAR_SELF);
   }
 
-  ReturnTuple Call_resumeAll(const BoxedValue& Var_self, const ParamTuple& params, const ValueTuple& args) final {
+  ReturnTuple Call_resumeAll(const ParamTuple& params, const ValueTuple& args) final {
     TRACE_FUNCTION("ThreadCondition.resumeAll")
     int error = 0;
     if ((error = pthread_cond_broadcast(&cond)) != 0) {
       FailError("Error resuming threads", error);
     }
-    return ReturnTuple(Var_self);
+    return ReturnTuple(VAR_SELF);
   }
 
-  ReturnTuple Call_resumeOne(const BoxedValue& Var_self, const ParamTuple& params, const ValueTuple& args) final {
+  ReturnTuple Call_resumeOne(const ParamTuple& params, const ValueTuple& args) final {
     TRACE_FUNCTION("ThreadCondition.resumeOne")
     int error = 0;
     if ((error = pthread_cond_signal(&cond)) != 0) {
       FailError("Error resuming thread", error);
     }
-    return ReturnTuple(Var_self);
+    return ReturnTuple(VAR_SELF);
   }
 
-  ReturnTuple Call_timedWait(const BoxedValue& Var_self, const ParamTuple& params, const ValueTuple& args) final {
+  ReturnTuple Call_timedWait(const ParamTuple& params, const ValueTuple& args) final {
     TRACE_FUNCTION("ThreadCondition.timedWait")
     int error = 0;
     const PrimFloat Var_arg1 = (args.At(0)).AsFloat();
@@ -106,22 +106,22 @@
     return ReturnTuple(Box_Bool(true));
   }
 
-  ReturnTuple Call_unlock(const BoxedValue& Var_self, const ParamTuple& params, const ValueTuple& args) final {
+  ReturnTuple Call_unlock(const ParamTuple& params, const ValueTuple& args) final {
     TRACE_FUNCTION("ThreadCondition.unlock")
     int error = 0;
     if ((error = pthread_mutex_unlock(&mutex)) != 0) {
       FailError("Error unlocking mutex", error);
     }
-    return ReturnTuple(Var_self);
+    return ReturnTuple(VAR_SELF);
   }
 
-  ReturnTuple Call_wait(const BoxedValue& Var_self, const ParamTuple& params, const ValueTuple& args) final {
+  ReturnTuple Call_wait(const ParamTuple& params, const ValueTuple& args) final {
     TRACE_FUNCTION("ThreadCondition.wait")
     int error = 0;
     if ((error = pthread_cond_wait(&cond, &mutex)) != 0) {
       FailError("Error waiting for condition", error);
     }
-    return ReturnTuple(Var_self);
+    return ReturnTuple(VAR_SELF);
   }
 
   void FailError(const std::string& context, int error) const {
@@ -150,12 +150,14 @@
   static auto& category = *new ExtCategory_ThreadCondition();
   return category;
 }
+
 S<Type_ThreadCondition> CreateType_ThreadCondition(Params<0>::Type params) {
   static const auto cached = S_get(new ExtType_ThreadCondition(CreateCategory_ThreadCondition(), Params<0>::Type()));
   return cached;
 }
+
 BoxedValue CreateValue_ThreadCondition(S<Type_ThreadCondition> parent) {
-  return BoxedValue(new ExtValue_ThreadCondition(parent));
+  return BoxedValue::New<ExtValue_ThreadCondition>(parent);
 }
 
 #ifdef ZEOLITE_PUBLIC_NAMESPACE
diff --git a/lib/util/.zeolite-module b/lib/util/.zeolite-module
--- a/lib/util/.zeolite-module
+++ b/lib/util/.zeolite-module
@@ -30,5 +30,9 @@
     source: "lib/util/src/Extension_SimpleOutput.cpp"
     categories: [SimpleOutput]
   }
+  category_source {
+    source: "lib/util/src/Extension_SpinlockMutex.cpp"
+    categories: [SpinlockMutex]
+  }
 ]
 mode: incremental {}
diff --git a/lib/util/interfaces.0rp b/lib/util/interfaces.0rp
new file mode 100644
--- /dev/null
+++ b/lib/util/interfaces.0rp
@@ -0,0 +1,29 @@
+/* -----------------------------------------------------------------------------
+Copyright 2021 Kevin P. Barry
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+----------------------------------------------------------------------------- */
+
+// Author: Kevin P. Barry [ta0kira@gmail.com]
+
+// Can be duplicated.
+@value interface Duplicate {
+  // Creates a duplicate that can be mutated without changing the original.
+  //
+  // Notes:
+  // - This does not need to be a deep copy. The only requirement is that
+  //   mutating functions can be called on the copy without counterintuitive
+  //   results. For example, if the object is a container, the duplicate could
+  //   be a copy of the structure, while still referring to the same objects.
+  duplicate () -> (#self)
+}
diff --git a/lib/util/mutex.0rp b/lib/util/mutex.0rp
--- a/lib/util/mutex.0rp
+++ b/lib/util/mutex.0rp
@@ -37,6 +37,14 @@
   @type new () -> (Mutex)
 }
 
+// A mutex with faster performance when lockouts are rare and/or very short.
+concrete SpinlockMutex {
+  refines Mutex
+
+  // Create a new mutex.
+  @type new () -> (Mutex)
+}
+
 // An automatic mutex lock.
 //
 // Notes:
diff --git a/lib/util/mutex.0rt b/lib/util/mutex.0rt
--- a/lib/util/mutex.0rt
+++ b/lib/util/mutex.0rt
@@ -16,12 +16,17 @@
 
 // Author: Kevin P. Barry [ta0kira@gmail.com]
 
-testcase "SimpleMutex tests" {
+testcase "Mutex tests" {
   success
 }
 
-unittest lockUnlock {
+unittest simpleLockUnlock {
   Mutex mutex <- SimpleMutex.new()
+  \ mutex.lock().unlock().lock().unlock()
+}
+
+unittest spinlockLockUnlock {
+  Mutex mutex <- SpinlockMutex.new()
   \ mutex.lock().unlock().lock().unlock()
 }
 
diff --git a/lib/util/src/Extension_Argv.cpp b/lib/util/src/Extension_Argv.cpp
--- a/lib/util/src/Extension_Argv.cpp
+++ b/lib/util/src/Extension_Argv.cpp
@@ -1,9 +1,12 @@
 /* -----------------------------------------------------------------------------
 Copyright 2020-2021 Kevin P. Barry
+
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at
+
     http://www.apache.org/licenses/LICENSE-2.0
+
 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -22,8 +25,6 @@
 namespace ZEOLITE_PUBLIC_NAMESPACE {
 #endif  // ZEOLITE_PUBLIC_NAMESPACE
 
-BoxedValue CreateValue_Argv(S<Type_Argv> parent, int st, int sz);
-
 namespace {
 extern const BoxedValue& Var_global;
 }  // namespace
@@ -44,18 +45,18 @@
   inline ExtValue_Argv(S<Type_Argv> p, int st, int sz)
     : Value_Argv(p), start(st), size(sz) {}
 
-  ReturnTuple Call_readAt(const BoxedValue& Var_self, const ParamTuple& params, const ValueTuple& args) final {
+  ReturnTuple Call_readAt(const ParamTuple& params, const ValueTuple& args) final {
     TRACE_FUNCTION("Argv.readAt")
     const PrimInt Var_arg1 = (args.At(0)).AsInt();
     return ReturnTuple(Box_String(Argv::GetArgAt(start + Var_arg1)));
   }
 
-  ReturnTuple Call_size(const BoxedValue& Var_self, const ParamTuple& params, const ValueTuple& args) final {
+  ReturnTuple Call_size(const ParamTuple& params, const ValueTuple& args) final {
     TRACE_FUNCTION("Argv.size")
     return ReturnTuple(Box_Int(GetSize()));
   }
 
-  ReturnTuple Call_subSequence(const BoxedValue& Var_self, const ParamTuple& params, const ValueTuple& args) final {
+  ReturnTuple Call_subSequence(const ParamTuple& params, const ValueTuple& args) final {
     TRACE_FUNCTION("Argv.subSequence")
     const PrimInt Var_arg1 = (args.At(0)).AsInt();
     const PrimInt Var_arg2 = (args.At(1)).AsInt();
@@ -65,7 +66,7 @@
     if (Var_arg2 < 0 || Var_arg1 + Var_arg2 > GetSize()) {
       FAIL() << "size " << Var_arg2 << " is invalid";
     }
-    return ReturnTuple(BoxedValue(new ExtValue_Argv(parent, start + Var_arg1, Var_arg2)));
+    return ReturnTuple(BoxedValue::New<ExtValue_Argv>(parent, start + Var_arg1, Var_arg2));
   }
 
   inline int GetSize() const { return size < 0 ? Argv::ArgCount() : size; }
@@ -75,19 +76,17 @@
 };
 
 namespace {
-const BoxedValue& Var_global = *new BoxedValue(new ExtValue_Argv(CreateType_Argv(Params<0>::Type()), 0, -1));
+const BoxedValue& Var_global = *new BoxedValue(BoxedValue::New<ExtValue_Argv>(CreateType_Argv(Params<0>::Type()), 0, -1));
 }  // namespace
 
 Category_Argv& CreateCategory_Argv() {
   static auto& category = *new ExtCategory_Argv();
   return category;
 }
+
 S<Type_Argv> CreateType_Argv(Params<0>::Type params) {
   static const auto cached = S_get(new ExtType_Argv(CreateCategory_Argv(), Params<0>::Type()));
   return cached;
-}
-BoxedValue CreateValue_Argv(S<Type_Argv> parent, int st, int sz) {
-  return BoxedValue(new ExtValue_Argv(parent, st, sz));
 }
 
 #ifdef ZEOLITE_PUBLIC_NAMESPACE
diff --git a/lib/util/src/Extension_MutexLock.cpp b/lib/util/src/Extension_MutexLock.cpp
--- a/lib/util/src/Extension_MutexLock.cpp
+++ b/lib/util/src/Extension_MutexLock.cpp
@@ -48,7 +48,7 @@
     TypeValue::Call(BoxedValue::Require(mutex), Function_Mutex_lock, ParamTuple(), ArgTuple());
   }
 
-  ReturnTuple Call_freeResource(const BoxedValue& Var_self, const ParamTuple& params, const ValueTuple& args) final {
+  ReturnTuple Call_freeResource(const ParamTuple& params, const ValueTuple& args) final {
     TRACE_FUNCTION("MutexLock.freeResource")
     TRACE_CREATION
     if (!BoxedValue::Present(mutex)) {
@@ -78,12 +78,14 @@
   static auto& category = *new ExtCategory_MutexLock();
   return category;
 }
+
 S<Type_MutexLock> CreateType_MutexLock(Params<0>::Type params) {
   static const auto cached = S_get(new ExtType_MutexLock(CreateCategory_MutexLock(), Params<0>::Type()));
   return cached;
 }
+
 BoxedValue CreateValue_MutexLock(S<Type_MutexLock> parent, const ValueTuple& args) {
-  return BoxedValue(new ExtValue_MutexLock(parent, args));
+  return BoxedValue::New<ExtValue_MutexLock>(parent, args);
 }
 
 #ifdef ZEOLITE_PUBLIC_NAMESPACE
diff --git a/lib/util/src/Extension_SimpleInput.cpp b/lib/util/src/Extension_SimpleInput.cpp
--- a/lib/util/src/Extension_SimpleInput.cpp
+++ b/lib/util/src/Extension_SimpleInput.cpp
@@ -1,9 +1,12 @@
 /* -----------------------------------------------------------------------------
 Copyright 2019-2021 Kevin P. Barry
+
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at
+
     http://www.apache.org/licenses/LICENSE-2.0
+
 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -44,13 +47,13 @@
 struct ExtValue_SimpleInput : public Value_SimpleInput {
   inline ExtValue_SimpleInput(S<Type_SimpleInput> p, const ValueTuple& args) : Value_SimpleInput(p) {}
 
-  ReturnTuple Call_pastEnd(const BoxedValue& Var_self, const ParamTuple& params, const ValueTuple& args) final {
+  ReturnTuple Call_pastEnd(const ParamTuple& params, const ValueTuple& args) final {
     TRACE_FUNCTION("SimpleInput.pastEnd")
     std::lock_guard<std::mutex> lock(mutex);
     return ReturnTuple(Box_Bool(zero_read));
   }
 
-  ReturnTuple Call_readBlock(const BoxedValue& Var_self, const ParamTuple& params, const ValueTuple& args) final {
+  ReturnTuple Call_readBlock(const ParamTuple& params, const ValueTuple& args) final {
     TRACE_FUNCTION("SimpleInput.readBlock")
     std::lock_guard<std::mutex> lock(mutex);
     const int size = args.At(0).AsInt();
@@ -72,13 +75,14 @@
 };
 
 namespace {
-const BoxedValue& Var_stdin = *new BoxedValue(new ExtValue_SimpleInput(CreateType_SimpleInput(Params<0>::Type()), ArgTuple()));
+const BoxedValue& Var_stdin = *new BoxedValue(BoxedValue::New<ExtValue_SimpleInput>(CreateType_SimpleInput(Params<0>::Type()), ArgTuple()));
 }  // namespace
 
 Category_SimpleInput& CreateCategory_SimpleInput() {
   static auto& category = *new ExtCategory_SimpleInput();
   return category;
 }
+
 S<Type_SimpleInput> CreateType_SimpleInput(Params<0>::Type params) {
   static const auto cached = S_get(new ExtType_SimpleInput(CreateCategory_SimpleInput(), Params<0>::Type()));
   return cached;
diff --git a/lib/util/src/Extension_SimpleMutex.cpp b/lib/util/src/Extension_SimpleMutex.cpp
--- a/lib/util/src/Extension_SimpleMutex.cpp
+++ b/lib/util/src/Extension_SimpleMutex.cpp
@@ -44,16 +44,16 @@
 struct ExtValue_SimpleMutex : public Value_SimpleMutex {
   inline ExtValue_SimpleMutex(S<Type_SimpleMutex> p) : Value_SimpleMutex(p) {}
 
-  ReturnTuple Call_lock(const BoxedValue& Var_self, const ParamTuple& params, const ValueTuple& args) final {
+  ReturnTuple Call_lock(const ParamTuple& params, const ValueTuple& args) final {
     TRACE_FUNCTION("SimpleMutex.lock")
     mutex.lock();
-    return ReturnTuple(Var_self);
+    return ReturnTuple(VAR_SELF);
   }
 
-  ReturnTuple Call_unlock(const BoxedValue& Var_self, const ParamTuple& params, const ValueTuple& args) final {
+  ReturnTuple Call_unlock(const ParamTuple& params, const ValueTuple& args) final {
     TRACE_FUNCTION("SimpleMutex.unlock")
     mutex.unlock();
-    return ReturnTuple(Var_self);
+    return ReturnTuple(VAR_SELF);
   }
 
   std::mutex mutex;
@@ -63,12 +63,14 @@
   static auto& category = *new ExtCategory_SimpleMutex();
   return category;
 }
+
 S<Type_SimpleMutex> CreateType_SimpleMutex(Params<0>::Type params) {
   static const auto cached = S_get(new ExtType_SimpleMutex(CreateCategory_SimpleMutex(), Params<0>::Type()));
   return cached;
 }
+
 BoxedValue CreateValue_SimpleMutex(S<Type_SimpleMutex> parent) {
-  return BoxedValue(new ExtValue_SimpleMutex(parent));
+  return BoxedValue::New<ExtValue_SimpleMutex>(parent);
 }
 
 #ifdef ZEOLITE_PUBLIC_NAMESPACE
diff --git a/lib/util/src/Extension_SimpleOutput.cpp b/lib/util/src/Extension_SimpleOutput.cpp
--- a/lib/util/src/Extension_SimpleOutput.cpp
+++ b/lib/util/src/Extension_SimpleOutput.cpp
@@ -1,9 +1,12 @@
 /* -----------------------------------------------------------------------------
 Copyright 2019-2021 Kevin P. Barry
+
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at
+
     http://www.apache.org/licenses/LICENSE-2.0
+
 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -66,14 +69,14 @@
   inline ExtValue_SimpleOutput(S<Type_SimpleOutput> p, S<Writer> w)
     : Value_SimpleOutput(p), writer(w) {}
 
-  ReturnTuple Call_flush(const BoxedValue& Var_self, const ParamTuple& params, const ValueTuple& args) final {
+  ReturnTuple Call_flush(const ParamTuple& params, const ValueTuple& args) final {
     TRACE_FUNCTION("SimpleOutput.flush")
     std::lock_guard<std::mutex> lock(mutex);
     writer->Flush();
     return ReturnTuple();
   }
 
-  ReturnTuple Call_write(const BoxedValue& Var_self, const ParamTuple& params, const ValueTuple& args) final {
+  ReturnTuple Call_write(const ParamTuple& params, const ValueTuple& args) final {
     TRACE_FUNCTION("SimpleOutput.write")
     const BoxedValue& Var_arg1 = (args.At(0));
     std::lock_guard<std::mutex> lock(mutex);
@@ -115,9 +118,9 @@
   std::ostringstream output;
 };
 
-const BoxedValue& Var_stdout = *new BoxedValue(new ExtValue_SimpleOutput(CreateType_SimpleOutput(Params<0>::Type()), S_get(new StreamWriter(std::cout))));
-const BoxedValue& Var_stderr = *new BoxedValue(new ExtValue_SimpleOutput(CreateType_SimpleOutput(Params<0>::Type()), S_get(new StreamWriter(std::cerr))));
-const BoxedValue& Var_error  = *new BoxedValue(new ExtValue_SimpleOutput(CreateType_SimpleOutput(Params<0>::Type()), S_get(new ErrorWriter())));
+const BoxedValue& Var_stdout = *new BoxedValue(BoxedValue::New<ExtValue_SimpleOutput>(CreateType_SimpleOutput(Params<0>::Type()), S_get(new StreamWriter(std::cout))));
+const BoxedValue& Var_stderr = *new BoxedValue(BoxedValue::New<ExtValue_SimpleOutput>(CreateType_SimpleOutput(Params<0>::Type()), S_get(new StreamWriter(std::cerr))));
+const BoxedValue& Var_error  = *new BoxedValue(BoxedValue::New<ExtValue_SimpleOutput>(CreateType_SimpleOutput(Params<0>::Type()), S_get(new ErrorWriter())));
 
 }  // namespace
 
@@ -125,6 +128,7 @@
   static auto& category = *new ExtCategory_SimpleOutput();
   return category;
 }
+
 S<Type_SimpleOutput> CreateType_SimpleOutput(Params<0>::Type params) {
   static const auto cached = S_get(new ExtType_SimpleOutput(CreateCategory_SimpleOutput(), Params<0>::Type()));
   return cached;
diff --git a/lib/util/src/Extension_SpinlockMutex.cpp b/lib/util/src/Extension_SpinlockMutex.cpp
new file mode 100644
--- /dev/null
+++ b/lib/util/src/Extension_SpinlockMutex.cpp
@@ -0,0 +1,79 @@
+/* -----------------------------------------------------------------------------
+Copyright 2021 Kevin P. Barry
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+----------------------------------------------------------------------------- */
+
+// Author: Kevin P. Barry [ta0kira@gmail.com]
+
+#include <atomic>
+
+#include "category-source.hpp"
+#include "Streamlined_SpinlockMutex.hpp"
+#include "Category_Mutex.hpp"
+#include "Category_SpinlockMutex.hpp"
+
+#ifdef ZEOLITE_PUBLIC_NAMESPACE
+namespace ZEOLITE_PUBLIC_NAMESPACE {
+#endif  // ZEOLITE_PUBLIC_NAMESPACE
+
+BoxedValue CreateValue_SpinlockMutex(S<Type_SpinlockMutex> parent);
+
+struct ExtCategory_SpinlockMutex : public Category_SpinlockMutex {
+};
+
+struct ExtType_SpinlockMutex : public Type_SpinlockMutex {
+  inline ExtType_SpinlockMutex(Category_SpinlockMutex& p, Params<0>::Type params) : Type_SpinlockMutex(p, params) {}
+
+  ReturnTuple Call_new(const S<TypeInstance>& Param_self, const ParamTuple& params, const ValueTuple& args) final {
+    TRACE_FUNCTION("SpinlockMutex.new")
+    return ReturnTuple(CreateValue_SpinlockMutex(CreateType_SpinlockMutex(Params<0>::Type())));
+  }
+};
+
+struct ExtValue_SpinlockMutex : public Value_SpinlockMutex {
+  inline ExtValue_SpinlockMutex(S<Type_SpinlockMutex> p) : Value_SpinlockMutex(p) {}
+
+  ReturnTuple Call_lock(const ParamTuple& params, const ValueTuple& args) final {
+    TRACE_FUNCTION("SpinlockMutex.lock")
+    while (flag.test_and_set(std::memory_order_acquire));
+    return ReturnTuple(VAR_SELF);
+  }
+
+  ReturnTuple Call_unlock(const ParamTuple& params, const ValueTuple& args) final {
+    TRACE_FUNCTION("SpinlockMutex.unlock")
+    flag.clear(std::memory_order_release);
+    return ReturnTuple(VAR_SELF);
+  }
+
+  std::atomic_flag flag = ATOMIC_FLAG_INIT;
+};
+
+Category_SpinlockMutex& CreateCategory_SpinlockMutex() {
+  static auto& category = *new ExtCategory_SpinlockMutex();
+  return category;
+}
+
+S<Type_SpinlockMutex> CreateType_SpinlockMutex(Params<0>::Type params) {
+  static const auto cached = S_get(new ExtType_SpinlockMutex(CreateCategory_SpinlockMutex(), Params<0>::Type()));
+  return cached;
+}
+
+BoxedValue CreateValue_SpinlockMutex(S<Type_SpinlockMutex> parent) {
+  return BoxedValue::New<ExtValue_SpinlockMutex>(parent);
+}
+
+#ifdef ZEOLITE_PUBLIC_NAMESPACE
+}  // namespace ZEOLITE_PUBLIC_NAMESPACE
+using namespace ZEOLITE_PUBLIC_NAMESPACE;
+#endif  // ZEOLITE_PUBLIC_NAMESPACE
diff --git a/src/Cli/RunCompiler.hs b/src/Cli/RunCompiler.hs
--- a/src/Cli/RunCompiler.hs
+++ b/src/Cli/RunCompiler.hs
@@ -146,8 +146,8 @@
 
 runCompiler resolver _ (CompileOptions _ is is2 ds es ep p m f) = mapM_ compileSingle ds where
   compileSingle d = do
-    as  <- fmap fixPaths $ mapCompilerM (resolveModule resolver (p </> d)) is
-    as2 <- fmap fixPaths $ mapCompilerM (resolveModule resolver (p </> d)) is2
+    as  <- fmap fixPaths $ mapCompilerM (autoDep (p </> d)) is
+    as2 <- fmap fixPaths $ mapCompilerM (autoDep (p </> d)) is2
     isConfigured <- isPathConfigured p d
     when (isConfigured && f == DoNotForce) $ do
       compilerErrorM $ "Module " ++ d ++ " has an existing configuration. " ++
@@ -166,6 +166,11 @@
     writeRecompile (p </> d) rm
     config <- getRecompilePath (p </> d)
     errorFromIO $ hPutStrLn stderr $ "*** Setup complete. Please edit " ++ config ++ " and recompile with zeolite -r. ***"
+  autoDep p2 i = do
+    isSystem <- isSystemModule resolver p2 i
+    if isSystem
+       then return i
+       else resolveModule resolver p2 i
 
 runRecompileCommon :: (PathIOHandler r, CompilerBackend b) => r -> b ->
   ForceMode -> Bool -> FilePath -> [FilePath] -> TrackedErrorsIO ()
diff --git a/src/Compilation/ScopeContext.hs b/src/Compilation/ScopeContext.hs
--- a/src/Compilation/ScopeContext.hs
+++ b/src/Compilation/ScopeContext.hs
@@ -114,5 +114,5 @@
 -- TODO: This is probably in the wrong module.
 builtinVariables :: TypeInstance -> Map.Map VariableName (VariableValue c)
 builtinVariables t = Map.fromList [
-    (VariableName "self",VariableValue [] ValueScope (ValueType RequiredValue $ singleType $ JustTypeInstance t) (VariableReadOnly []))
+    (VariableSelf,VariableValue [] ValueScope (ValueType RequiredValue $ singleType $ JustTypeInstance t) (VariableReadOnly []))
   ]
diff --git a/src/CompilerCxx/CxxFiles.hs b/src/CompilerCxx/CxxFiles.hs
--- a/src/CompilerCxx/CxxFiles.hs
+++ b/src/CompilerCxx/CxxFiles.hs
@@ -136,7 +136,12 @@
     guardNamespace
       | isStaticNamespace $ getCategoryNamespace t = show (getCategoryNamespace t) ++ "_"
       | otherwise = ""
-    labels = onlyCodes $ map label $ filter ((== name) . sfType) $ getCategoryFunctions t
+    functions = sortBy compareName $ filter ((== name) . sfType) $ getCategoryFunctions t
+    compareName x y = sfName x `compare` sfName y
+    categoryFunctions = filter ((== CategoryScope) . sfScope) functions
+    typeFunctions = filter ((== TypeScope) . sfScope) functions
+    valueFunctions = filter ((== ValueScope) . sfScope) functions
+    labels = onlyCodes $ map label $ categoryFunctions ++ typeFunctions ++ valueFunctions
     label f = "extern " ++ functionLabelType f ++ " " ++ functionName f ++ ";"
     collection
       | isValueConcrete t = emptyCode
@@ -499,7 +504,7 @@
     ]
   declareValueOverrides = onlyCodes [
       "  std::string CategoryName() const final;",
-      "  ReturnTuple Dispatch(const BoxedValue& self, const ValueFunction& label, const ParamTuple& params, const ValueTuple& args) final;"
+      "  ReturnTuple Dispatch(const ValueFunction& label, const ParamTuple& params, const ValueTuple& args) final;"
     ]
 
   defineCategoryOverrides t fs = return $ mconcat [
@@ -529,7 +534,7 @@
       params = map vpParam $ getCategoryParams t
   defineValueOverrides t fs = return $ mconcat [
       onlyCode $ "std::string " ++ className ++ "::CategoryName() const { return parent->CategoryName(); }",
-      onlyCode $ "ReturnTuple " ++ className ++ "::Dispatch(const BoxedValue& self, const ValueFunction& label, const ParamTuple& params, const ValueTuple& args) {",
+      onlyCode $ "ReturnTuple " ++ className ++ "::Dispatch(const ValueFunction& label, const ParamTuple& params, const ValueTuple& args) {",
       createFunctionDispatch (getCategoryName t) ValueScope fs,
       onlyCode $ "}"
     ] where
@@ -737,7 +742,7 @@
     | s == TypeScope     = "  using CallType = ReturnTuple(" ++ typeName n ++
                            "::*)(const S<TypeInstance>&, const ParamTuple&, const ValueTuple&);"
     | s == ValueScope    = "  using CallType = ReturnTuple(" ++ valueName n ++
-                           "::*)(const BoxedValue&, const ParamTuple&, const ValueTuple&);"
+                           "::*)(const ParamTuple&, const ValueTuple&);"
     | otherwise = undefined
   name f
     | s == CategoryScope = categoryName n ++ "::" ++ callName f
@@ -767,12 +772,12 @@
   args
     | s == CategoryScope = "params, args"
     | s == TypeScope     = "self, params, args"
-    | s == ValueScope    = "self, params, args"
+    | s == ValueScope    = "params, args"
     | otherwise = undefined
   fallback
     | s == CategoryScope = "  return TypeCategory::Dispatch(label, params, args);"
     | s == TypeScope     = "  return TypeInstance::Dispatch(self, label, params, args);"
-    | s == ValueScope    = "  return TypeValue::Dispatch(self, label, params, args);"
+    | s == ValueScope    = "  return TypeValue::Dispatch(label, params, args);"
     | otherwise = undefined
 
 createCanConvertFrom :: AnyCategory c -> CompiledData [String]
@@ -936,7 +941,7 @@
   onlyCodes [
       "BoxedValue " ++ valueCreator (getCategoryName t) ++ "(S<" ++ typeName (getCategoryName t) ++ "> parent, " ++
       "const ValueTuple& args) {",
-      "  return BoxedValue(new " ++ className ++ "(parent, args));",
+      "  return BoxedValue::New<" ++ className ++ ">(parent, args);",
       "}"
     ]
 
diff --git a/src/CompilerCxx/Naming.hs b/src/CompilerCxx/Naming.hs
--- a/src/CompilerCxx/Naming.hs
+++ b/src/CompilerCxx/Naming.hs
@@ -108,7 +108,8 @@
 paramName p = "Param_" ++ tail (show p) -- Remove leading '#'.
 
 variableName :: VariableName -> String
-variableName v = "Var_" ++ show v
+variableName VariableSelf = "VAR_SELF"
+variableName v            = "Var_" ++ show v
 
 hiddenVariableName :: VariableName -> String
 hiddenVariableName v = "Internal_" ++ show v
diff --git a/src/CompilerCxx/Procedure.hs b/src/CompilerCxx/Procedure.hs
--- a/src/CompilerCxx/Procedure.hs
+++ b/src/CompilerCxx/Procedure.hs
@@ -76,7 +76,7 @@
       "(const S<TypeInstance>& Param_self, const ParamTuple& params, const ValueTuple& args)"
     | sfScope f == ValueScope =
       "ReturnTuple " ++ name ++
-      "(const BoxedValue& Var_self, const ParamTuple& params, const ValueTuple& args)"
+      "(const ParamTuple& params, const ValueTuple& args)"
     | otherwise = undefined
 
 data CxxFunctionType =
@@ -134,7 +134,7 @@
         "(const S<TypeInstance>& Param_self, const ParamTuple& params, const ValueTuple& args)" ++ final ++ " {"
       | s == ValueScope =
         returnType ++ " " ++ prefix ++ name ++
-        "(const BoxedValue& Var_self, const ParamTuple& params, const ValueTuple& args)" ++ final ++ " {"
+        "(const ParamTuple& params, const ValueTuple& args)" ++ final ++ " {"
       | otherwise = undefined
     returnType = "ReturnTuple"
     setProcedureTrace
@@ -329,6 +329,12 @@
 compileStatement (NoValueExpression _ v) = compileVoidExpression v
 compileStatement (MarkReadOnly c vs) = mapM_ (\v -> csSetReadOnly (UsedVariable c v)) vs
 compileStatement (MarkHidden   c vs) = mapM_ (\v -> csSetHidden   (UsedVariable c v)) vs
+compileStatement (ValidateRefs c vs) = mapM_ validate vs where
+  validate n = do
+    (VariableValue _ _ t _) <- csGetVariable (UsedVariable c n)
+    let e = readStoredVariable False t (variableName n)
+    maybeSetTrace c
+    csWrite [useAsUnwrapped e ++ ".Validate(\"" ++ show n ++ "\");"]
 compileStatement (RawCodeLine s) = csWrite [s]
 
 compileRegularInit :: (Ord c, Show c, CollectErrorsM m,
@@ -429,7 +435,7 @@
   getAndIndentOutput ctx >>= csWrite
   csWrite $ ["{"] ++ u' ++ ["}"]
   csWrite ["}"]
-compileIteratedLoop (TraverseLoop c1 e c2 a (Procedure c3 ss)) = "In compilation of traverse at " ++ formatFullContext c1 ??> do
+compileIteratedLoop (TraverseLoop c1 e c2 a (Procedure c3 ss) u) = "In compilation of traverse at " ++ formatFullContext c1 ??> do
   (Positional ts,e') <- compileExpression e
   checkContainer ts
   r <- csResolver
@@ -454,9 +460,12 @@
   compileStatement $ NoValueExpression [] $ WithScope $ ScopedBlock []
     (Procedure [] [RawCodeLine $ variableStoredType currType ++ " " ++ currVar ++ " = " ++ writeStoredVariable currType e' ++ ";"]) Nothing []
     (NoValueExpression [] $ Loop $ WhileLoop [] (Expression [] currPresent [])
-      (Procedure c3 (assnGet:ss))
+      (Procedure c3 (assnGet:(ss ++ update)))
       (Just $ Procedure [] [RawCodeLine $ currVar ++ " = " ++ writeStoredVariable currType exprNext ++ ";"]))
     where
+      update = case u of
+                    Just (Procedure _ ss2) -> ss2
+                    _                      -> []
       checkContainer [_] = return ()
       checkContainer ts =
         compilerErrorM $ "Expected exactly one Order<?> value but got " ++
@@ -947,7 +956,7 @@
         show (sfName f) ++ ") inferred as " ++ show t ++ " at " ++ formatFullContext c2
     backgroundMessage _ = return ()
     assemble Nothing _ ValueScope ValueScope ps2 es2 =
-      return $ callName (sfName f) ++ "(Var_self, " ++ ps2 ++ ", " ++ es2 ++ ")"
+      return $ callName (sfName f) ++ "(" ++ ps2 ++ ", " ++ es2 ++ ")"
     assemble Nothing _ TypeScope TypeScope ps2 es2 =
       return $ callName (sfName f) ++ "(Param_self, " ++ ps2 ++ ", " ++ es2 ++ ")"
     assemble Nothing scoped ValueScope TypeScope ps2 es2 =
diff --git a/src/Parser/Procedure.hs b/src/Parser/Procedure.hs
--- a/src/Parser/Procedure.hs
+++ b/src/Parser/Procedure.hs
@@ -94,8 +94,13 @@
     noKeywords
     b <- lowerChar
     e <- sepAfter $ many alphaNumChar
-    return $ VariableName (b:e)
+    return $ boxVariableName (b:e)
 
+boxVariableName :: String -> VariableName
+boxVariableName n
+  | n == "self" = VariableSelf
+  | otherwise   = VariableName n
+
 instance ParseFromSource (InputValue SourceContext) where
   sourceParser = labeled "input variable" $ variable <|> discard where
     variable = do
@@ -172,8 +177,9 @@
       c <- getSourceContext
       e <- sourceParser
       return $ NoValueExpression [c] e
-    parsePragma = do
-      p <- pragmaReadOnly <|> pragmaHidden <|> unknownPragma
+    parsePragma = ((pragmaReadOnly <|> pragmaHidden) >>= markPragma) <|>
+                  pragmaValidateRefs <|> unknownPragma
+    markPragma p =
       case p of
            PragmaMarkVars c ReadOnly vs -> return $ MarkReadOnly c vs
            PragmaMarkVars c Hidden   vs -> return $ MarkHidden   c vs
@@ -234,10 +240,6 @@
       p <- between (sepAfter $ string_ "{") (sepAfter $ string_ "}") sourceParser
       u <- fmap Just parseUpdate <|> return Nothing
       return $ WhileLoop [c] i p u
-      where
-        parseUpdate = do
-          kwUpdate
-          between (sepAfter $ string_ "{") (sepAfter $ string_ "}") sourceParser
     trav = labeled "traverse" $ do
       c1 <- getSourceContext
       kwTraverse
@@ -248,7 +250,11 @@
       a <- sourceParser
       sepAfter_ $ string ")"
       p <- between (sepAfter $ string_ "{") (sepAfter $ string_ "}") sourceParser
-      return $ TraverseLoop [c1] e [c2] a p
+      u <- fmap Just parseUpdate <|> return Nothing
+      return $ TraverseLoop [c1] e [c2] a p u
+    parseUpdate = do
+      kwUpdate
+      between (sepAfter $ string_ "{") (sepAfter $ string_ "}") sourceParser
 
 instance ParseFromSource (ScopedBlock SourceContext) where
   sourceParser = scoped <|> justCleanup where
@@ -535,7 +541,7 @@
     builtinValue = do
       c <- getSourceContext
       n <- builtinValues
-      return $ NamedVariable (OutputValue [c] (VariableName n))
+      return $ NamedVariable (OutputValue [c] (boxVariableName n))
     sourceContext = do
       pragma <- pragmaSourceContext
       case pragma of
@@ -716,3 +722,9 @@
   parseAt c = do
     vs <- labeled "variable names" $ sepBy sourceParser (sepAfter $ string ",")
     return $ PragmaMarkVars [c] Hidden vs
+
+pragmaValidateRefs :: TextParser (Statement SourceContext)
+pragmaValidateRefs = autoPragma "ValidateRefs" $ Right parseAt where
+  parseAt c = do
+    vs <- labeled "variable names" $ sepBy sourceParser (sepAfter $ string ",")
+    return $ ValidateRefs [c] vs
diff --git a/src/Types/Procedure.hs b/src/Types/Procedure.hs
--- a/src/Types/Procedure.hs
+++ b/src/Types/Procedure.hs
@@ -115,11 +115,13 @@
 data VariableName =
   VariableName {
     vnName :: String
-  }
+  } |
+  VariableSelf
   deriving (Eq,Ord)
 
 instance Show VariableName where
   show (VariableName n) = n
+  show VariableSelf     = "self"
 
 data InputValue c =
   InputValue {
@@ -174,6 +176,7 @@
   NoValueExpression [c] (VoidExpression c) |
   MarkReadOnly [c] [VariableName] |
   MarkHidden [c] [VariableName] |
+  ValidateRefs [c] [VariableName] |
   RawCodeLine String
   deriving (Show)
 
@@ -193,6 +196,7 @@
 getStatementContext (NoValueExpression c _) = c
 getStatementContext (MarkReadOnly c _)      = c
 getStatementContext (MarkHidden c _)        = c
+getStatementContext (ValidateRefs c _)      = c
 getStatementContext (RawCodeLine _)         = []
 
 data Assignable c =
@@ -225,7 +229,7 @@
 
 data IteratedLoop c =
   WhileLoop [c] (Expression c) (Procedure c) (Maybe (Procedure c)) |
-  TraverseLoop [c] (Expression c) [c] (Assignable c) (Procedure c)
+  TraverseLoop [c] (Expression c) [c] (Assignable c) (Procedure c) (Maybe (Procedure c))
   deriving (Show)
 
 data ScopedBlock c =
diff --git a/tests/cli-tests.sh b/tests/cli-tests.sh
--- a/tests/cli-tests.sh
+++ b/tests/cli-tests.sh
@@ -107,6 +107,11 @@
 }
 
 
+test_simulate_refs() {
+  do_zeolite -p "$ZEOLITE_PATH" -r tests/simulate-refs -f
+}
+
+
 test_tests_only() {
   local output=$(do_zeolite -p "$ZEOLITE_PATH" -r tests/tests-only -f || true)
   if ! echo "$output" | egrep -q 'Type1 .+ visible category'; then
@@ -349,6 +354,22 @@
 }
 
 
+test_example_random() {
+  local binary="$ZEOLITE_PATH/example/random/RandomDemo"
+  rm -f "$binary"
+  local temp=$(execute mktemp)
+  do_zeolite -p "$ZEOLITE_PATH" -r example/random -f
+  execute_noredir "$binary" 5 > "$temp"
+  local output=$(cat "$temp")
+  rm -f "$temp"
+  if [[ $(echo "$output" | wc -l) -lt 5 ]]; then
+    show_message "Expected more output from RandomDemo:"
+    echo "$output" 1>&2
+    return 1
+  fi
+}
+
+
 run_all() {
   ZEOLITE_PATH=$(do_zeolite --get-path | grep '^/')
   echo 1>&2
@@ -385,6 +406,7 @@
   test_bad_path
   test_check_defs
   test_leak_check
+  test_simulate_refs
   test_tests_only
   test_tests_only2
   test_tests_only3
@@ -402,6 +424,7 @@
   test_example_hello
   test_example_parser
   test_example_primes
+  test_example_random
 )
 
 run_all "${ALL_TESTS[@]}" 1>&2
diff --git a/tests/leak-check/README.md b/tests/leak-check/README.md
--- a/tests/leak-check/README.md
+++ b/tests/leak-check/README.md
@@ -67,3 +67,20 @@
 
 You should see `no race conditions this time` upon success. Any sort of error
 message means a crash, and thus a test failure.
+
+---
+
+A more comprehensive test involves leaving `LeakTest` running for an hour or so.
+
+```shell
+$ZEOLITE_PATH/tests/leak-check/LeakTest forever
+```
+
+This will cause `LeakCheck` to run for a very long time, while attempting to
+leak a few MB of memory each iteration. Since this is a combination of the other
+two tests, there isn't any point running this if either of the other two fail.
+
+If the memory size of the running `LeakCheck` process increases over time and
+doesn't go back down, there is a memory leak induced by a race condition. The
+memory size will vary up and down for the first few minutes, but after 5-10
+minutes it should be obvious if the size is increasing.
diff --git a/tests/leak-check/leak-check.0rx b/tests/leak-check/leak-check.0rx
--- a/tests/leak-check/leak-check.0rx
+++ b/tests/leak-check/leak-check.0rx
@@ -49,19 +49,26 @@
         .writeTo(SimpleOutput.stderr())
 
     optional LeakTest original <- LeakTest{ Vector:createSize<Int>(size) }
-
-    Vector<Thread> threads <- Vector:create<Thread>()
+    weak LeakTest checkedValue <- original
+    $ReadOnly[checkedValue]$
 
-    traverse (Counter.zeroIndexed(barriers.size()-1) -> Int j) {
-      \ threads.push(ProcessThread.from(#f.create(original,barriers.readAt(j+1))).start())
-    }
+    scoped {
+      Vector<Thread> threads <- Vector:create<Thread>()
+    } in {
+      traverse (Counter.zeroIndexed(barriers.size()-1) -> Int j) {
+        \ threads.push(ProcessThread.from(#f.create(original,barriers.readAt(j+1))).start())
+      }
 
-    \ barriers.readAt(0).wait()
-    original <- empty
+      \ barriers.readAt(0).wait()
+      original <- empty
 
-    traverse (threads.defaultOrder() -> Thread thread) {
-      \ thread.join()
+      traverse (threads.defaultOrder() -> Thread thread) {
+        \ thread.join()
+      }
     }
+
+    // Make sure that threads are out of scope before validating.
+    $ValidateRefs[checkedValue]$
   }
 }
 
diff --git a/tests/modified-storage.0rt b/tests/modified-storage.0rt
--- a/tests/modified-storage.0rt
+++ b/tests/modified-storage.0rt
@@ -262,3 +262,44 @@
 
   call () {}
 }
+
+
+testcase "ValidateRefs succeeds" {
+  success
+}
+
+unittest boxed {
+  String value <- "message"
+  weak String value2 <- value
+  $ValidateRefs[value,value2]$
+}
+
+unittest int {
+  Int value <- 1
+  weak Int value2 <- value
+  $ValidateRefs[value,value2]$
+}
+
+unittest char {
+  Char value <- 'a'
+  weak Char value2 <- value
+  $ValidateRefs[value,value2]$
+}
+
+unittest bool {
+  Bool value <- false
+  weak Bool value2 <- value
+  $ValidateRefs[value,value2]$
+}
+
+unittest float {
+  Float value <- 1.0
+  weak Float value2 <- value
+  $ValidateRefs[value,value2]$
+}
+
+unittest emptyVal {
+  optional all value <- empty
+  weak all value2 <- value
+  $ValidateRefs[value,value2]$
+}
diff --git a/tests/module-only4/common-source.cpp b/tests/module-only4/common-source.cpp
--- a/tests/module-only4/common-source.cpp
+++ b/tests/module-only4/common-source.cpp
@@ -48,7 +48,7 @@
   inline ExtValue_Type1(S<Type_Type1> p, const ValueTuple& args)
     : Value_Type1(p), value(args.At(0)) {}
 
-  ReturnTuple Call_get(const BoxedValue& Var_self, const ParamTuple& params, const ValueTuple& args) {
+  ReturnTuple Call_get(const ParamTuple& params, const ValueTuple& args) {
     TRACE_FUNCTION("Type1.get")
     return ReturnTuple(TypeValue::Call(value, Function_Type2_get, ParamTuple(), ArgTuple()));
   }
@@ -60,12 +60,14 @@
   static auto& category = *new ExtCategory_Type1();
   return category;
 }
+
 S<Type_Type1> CreateType_Type1(Params<0>::Type params) {
   static const auto cached = S_get(new ExtType_Type1(CreateCategory_Type1(), Params<0>::Type()));
   return cached;
 }
+
 BoxedValue CreateValue_Type1(S<Type_Type1> parent, const ValueTuple& args) {
-  return BoxedValue(new ExtValue_Type1(parent, args));
+  return BoxedValue::New<ExtValue_Type1>(parent, args);
 }
 
 #ifdef ZEOLITE_PRIVATE_NAMESPACE
@@ -97,7 +99,7 @@
   inline ExtValue_Type3(S<Type_Type3> p, const ValueTuple& args)
     : Value_Type3(p), value(args.At(0)) {}
 
-  ReturnTuple Call_get(const BoxedValue& Var_self, const ParamTuple& params, const ValueTuple& args) {
+  ReturnTuple Call_get(const ParamTuple& params, const ValueTuple& args) {
     TRACE_FUNCTION("Type3.get")
     return ReturnTuple(TypeValue::Call(value, Function_Type2_get, ParamTuple(), ArgTuple()));
   }
@@ -109,12 +111,14 @@
   static auto& category = *new ExtCategory_Type3();
   return category;
 }
+
 S<Type_Type3> CreateType_Type3(Params<0>::Type params) {
   static const auto cached = S_get(new ExtType_Type3(CreateCategory_Type3(), Params<0>::Type()));
   return cached;
 }
+
 BoxedValue CreateValue_Type3(S<Type_Type3> parent, const ValueTuple& args) {
-  return BoxedValue(new ExtValue_Type3(parent, args));
+  return BoxedValue::New<ExtValue_Type3>(parent, args);
 }
 
 #ifdef ZEOLITE_PUBLIC_NAMESPACE
diff --git a/tests/module-only4/tests.0rt b/tests/module-only4/tests.0rt
new file mode 100644
--- /dev/null
+++ b/tests/module-only4/tests.0rt
@@ -0,0 +1,62 @@
+/* -----------------------------------------------------------------------------
+Copyright 2020 Kevin P. Barry
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+----------------------------------------------------------------------------- */
+
+// Author: Kevin P. Barry [ta0kira@gmail.com]
+
+testcase "ModuleOnly using ModuleOnly" {
+  success
+}
+
+unittest test {
+  \ Test.run()
+}
+
+define Test {
+  run () {
+    Type1 value <- Type1.create()
+    String message <- value.get()
+    if (message != "message") {
+      fail(message)
+    }
+  }
+}
+
+concrete Test {
+  @type run () -> ()
+}
+
+
+testcase "non-ModuleOnly using ModuleOnly" {
+  success
+}
+
+unittest test {
+  \ Test.run()
+}
+
+define Test {
+  run () {
+    Type3 value <- Type3.create()
+    String message <- value.get()
+    if (message != "message") {
+      fail(message)
+    }
+  }
+}
+
+concrete Test {
+  @type run () -> ()
+}
diff --git a/tests/regressions.0rt b/tests/regressions.0rt
--- a/tests/regressions.0rt
+++ b/tests/regressions.0rt
@@ -313,3 +313,16 @@
 unittest test {
   _ <- Type.sideEffects()
 }
+
+
+testcase "Issue #185 fix doesn't break count matching" {
+  // https://github.com/ta0kira/zeolite/issues/185
+  error
+  require "mismatch"
+  require "Int"
+  require "_"
+}
+
+unittest test {
+  _, _ <- 1
+}
diff --git a/tests/simulate-refs/.zeolite-module b/tests/simulate-refs/.zeolite-module
new file mode 100644
--- /dev/null
+++ b/tests/simulate-refs/.zeolite-module
@@ -0,0 +1,12 @@
+root: "../.."
+path: "tests/simulate-refs"
+private_deps: [
+  "lib/container"
+  "lib/math"
+  "lib/thread"
+  "lib/util"
+]
+mode: binary {
+  category: SimulateRefs
+  function: run
+}
diff --git a/tests/simulate-refs/README.md b/tests/simulate-refs/README.md
new file mode 100644
--- /dev/null
+++ b/tests/simulate-refs/README.md
@@ -0,0 +1,23 @@
+# Zeolite Reference Simulation
+
+This module was created to investigate a race condition in the implementation
+of `weak`. (See [#186](https://github.com/ta0kira/zeolite/issues/186).) It
+models the previous behaviors (before the fix) of `BoxedValue` and `WeakValue`
+using state machines that are stochastically interleaved, in an attempt to
+determine which sequences of events caused the memory leak.
+
+This simulation *has not* been updated to reflect the fixed behavior of `weak`
+references, and therefore will show "error" output describing the situation that
+previously led to a memory leak.
+
+Note that this simulation code isn't specific to Zeolite; it could have been
+written in any other language. The errors output by the simulation are
+predictions made by the model, rather than actual runtime errors in the program.
+
+To compile and run the simulation:
+
+```shell
+ZEOLITE_PATH=$(zeolite --get-path)
+zeolite -p $ZEOLITE_PATH -r tests/simulate-refs
+$ZEOLITE_PATH/tests/simulate-refs/SimulateRefs
+```
diff --git a/tests/simulate-refs/main.0rx b/tests/simulate-refs/main.0rx
new file mode 100644
--- /dev/null
+++ b/tests/simulate-refs/main.0rx
@@ -0,0 +1,79 @@
+/* -----------------------------------------------------------------------------
+Copyright 2021 Kevin P. Barry
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+----------------------------------------------------------------------------- */
+
+// Author: Kevin P. Barry [ta0kira@gmail.com]
+
+concrete SimulateRefs {
+  @type run () -> ()
+}
+
+define SimulateRefs {
+  @category Int errorLimit <- 10
+  @category RandomUniform random <- RandomUniform.new(0.0,1.0).setSeed(Realtime.monoSeconds().asInt())
+
+  run () {
+    traverse (Counter.unlimited() -> Int i) {
+      \ LazyStream<Formatted>.new()
+          .append("Iteration ")
+          .append(i)
+          .append("\n")
+          .writeTo(SimpleOutput.stderr())
+
+      if (!runOnce() && (errorLimit <- errorLimit-1) == 0) {
+        break
+      }
+    }
+  }
+
+  @type getRoutines (ReferenceState) -> (DefaultOrder<StateMachine>)
+  getRoutines (state) {
+    return Vector:create<StateMachine>()
+        .append(SharedRoutine.new("shared1",state))
+        .append(WeakRoutine.new("weak1",state))
+        .append(WeakRoutine.new("weak2",state))
+  }
+
+  @type runOnce () -> (Bool)
+  runOnce () {
+    ReferenceState state <- ReferenceState.new()
+    \ StateExecutor:multiplexStates(getRoutines(state),random)
+
+    scoped {
+      optional String error <- state.getError()
+      if (!present(error) && (state.hasStrong() || state.hasWeak())) {
+        error <- "expected zero final references"
+      }
+    } in if (present(error)) {
+      \ LazyStream<Formatted>.new()
+          .append("Error in final state: ")
+          .append(require(error))
+          .append("\nFinal state: ")
+          .append(state)
+          .append("\n")
+          .writeTo(SimpleOutput.stdout())
+      traverse (state.getOperations() -> Formatted operation) {
+        \ LazyStream<Formatted>.new()
+            .append("  ")
+            .append(operation)
+            .append("\n")
+            .writeTo(SimpleOutput.stdout())
+      }
+      return false
+    } else {
+      return true
+    }
+  }
+}
diff --git a/tests/simulate-refs/ref-state.0rp b/tests/simulate-refs/ref-state.0rp
new file mode 100644
--- /dev/null
+++ b/tests/simulate-refs/ref-state.0rp
@@ -0,0 +1,48 @@
+/* -----------------------------------------------------------------------------
+Copyright 2021 Kevin P. Barry
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+----------------------------------------------------------------------------- */
+
+// Author: Kevin P. Barry [ta0kira@gmail.com]
+
+$ModuleOnly$
+
+concrete ReferenceState {
+  refines Formatted
+
+  @type new () -> (ReferenceState)
+
+  @value addOperation<#x> (String) -> ()
+  @value getOperations () -> (optional Order<Formatted>)
+
+  @value addStrong () -> (Int)
+  @value remStrong () -> (Int)
+  @value hasStrong () -> (Bool)
+
+  @value addWeak () -> (Int)
+  @value remWeak () -> (Int)
+  @value getWeak () -> (Int)
+  @value hasWeak () -> (Bool)
+
+  @value addLock () -> (Int)
+  @value remLock () -> (Int)
+  @value convertLock () -> (Int)
+  @type lockMod () -> (Int)
+
+  @value kill () -> ()
+  @value isAlive () -> (Bool)
+  @value cleanupWeak () -> ()
+
+  @value getError () -> (optional String)
+}
diff --git a/tests/simulate-refs/ref-state.0rx b/tests/simulate-refs/ref-state.0rx
new file mode 100644
--- /dev/null
+++ b/tests/simulate-refs/ref-state.0rx
@@ -0,0 +1,132 @@
+/* -----------------------------------------------------------------------------
+Copyright 2021 Kevin P. Barry
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+----------------------------------------------------------------------------- */
+
+// Author: Kevin P. Barry [ta0kira@gmail.com]
+
+define ReferenceState {
+  $ReadOnly[lockMod]$
+
+  @category Int lockMod <- \x1000
+
+  @value Int strongCount
+  @value Int weakCount
+  @value Int aliveCleanup
+  @value Int weakCleanup
+  @value Vector<Formatted> operations
+
+  new () {
+    return ReferenceState{ 0, 0, 0, 0, Vector:create<Formatted>() }
+  }
+
+  addOperation (name) {
+    \ operations.append(String.builder()
+        .append(name)
+        .append("[")
+        .append(typename<#x>().formatted())
+        .append("]: ")
+        .append(formatted())
+        .build())
+  }
+
+  getOperations () {
+    return operations.defaultOrder()
+  }
+
+  addStrong () {
+    return (strongCount <- strongCount+1)
+  }
+
+  remStrong () {
+    return (strongCount <- strongCount-1)
+  }
+
+  hasStrong () {
+    return strongCount > 0
+  }
+
+  addWeak () {
+    return (weakCount <- weakCount+1)
+  }
+
+  remWeak () {
+    return (weakCount <- weakCount-1)
+  }
+
+  getWeak () {
+    return weakCount
+  }
+
+  hasWeak () {
+    return weakCount > 0
+  }
+
+  addLock () {
+    return (strongCount <- strongCount+lockMod)
+  }
+
+  remLock () {
+    return (strongCount <- strongCount-lockMod)
+  }
+
+  convertLock () {
+    return (strongCount <- strongCount-(lockMod-1))
+  }
+
+  lockMod () {
+    return lockMod
+  }
+
+  kill () {
+    aliveCleanup <- aliveCleanup+1
+  }
+
+  isAlive () {
+    return aliveCleanup == 0
+  }
+
+  cleanupWeak () {
+    weakCleanup <- weakCleanup+1
+  }
+
+  getError () {
+    if (aliveCleanup > 1) {
+      return "object freed multiple times"
+    }
+    if (weakCleanup > 1) {
+      return "counters freed multiple times"
+    }
+    if (aliveCleanup > 0 && strongCount > 0) {
+      return "object freed while still referenced"
+    }
+    if (aliveCleanup == 0 && strongCount == 0) {
+      return "object not cleaned up after last reference"
+    }
+    return empty
+  }
+
+  formatted () {
+    return String.builder()
+        .append("S: ")
+        .append(strongCount.formatted())
+        .append(" W: ")
+        .append(weakCount.formatted())
+        .append(" AC: ")
+        .append(aliveCleanup.formatted())
+        .append(" WC: ")
+        .append(weakCleanup.formatted())
+        .build()
+  }
+}
diff --git a/tests/simulate-refs/shared.0rp b/tests/simulate-refs/shared.0rp
new file mode 100644
--- /dev/null
+++ b/tests/simulate-refs/shared.0rp
@@ -0,0 +1,24 @@
+/* -----------------------------------------------------------------------------
+Copyright 2021 Kevin P. Barry
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+----------------------------------------------------------------------------- */
+
+// Author: Kevin P. Barry [ta0kira@gmail.com]
+
+$ModuleOnly$
+
+concrete SharedRoutine {
+  @type new (String,ReferenceState) -> (StateMachine)
+  @type alreadyStrong (String,ReferenceState) -> (StateMachine)
+}
diff --git a/tests/simulate-refs/shared.0rx b/tests/simulate-refs/shared.0rx
new file mode 100644
--- /dev/null
+++ b/tests/simulate-refs/shared.0rx
@@ -0,0 +1,132 @@
+/* -----------------------------------------------------------------------------
+Copyright 2021 Kevin P. Barry
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+----------------------------------------------------------------------------- */
+
+// Author: Kevin P. Barry [ta0kira@gmail.com]
+
+define SharedRoutine {
+  new (name,state) {
+    if (state.addStrong() == 1) {
+      \ state.addWeak()
+    }
+    return DropSharedEnter.new(name,state)
+  }
+
+  alreadyStrong (name,state) {
+    return DropSharedEnter.new(name,state)
+  }
+}
+
+
+concrete DropSharedEnter {
+  @type new (String,ReferenceState) -> (StateMachine)
+}
+
+define DropSharedEnter {
+  $ReadOnly[name,state]$
+
+  refines StateMachine
+
+  @value String name
+  @value ReferenceState state
+
+  new (name,state) {
+    return DropSharedEnter{ name, state }
+  }
+
+  transition () {
+    \ state.addOperation<#self>(name)
+    if (state.remStrong() == 0) {
+      return DropSharedFreeObject.new(name,state)
+    } else {
+      return empty
+    }
+  }
+}
+
+
+concrete DropSharedFreeObject {
+  @type new (String,ReferenceState) -> (StateMachine)
+}
+
+define DropSharedFreeObject {
+  $ReadOnly[name,state]$
+
+  refines StateMachine
+
+  @value String name
+  @value ReferenceState state
+
+  new (name,state) {
+    return DropSharedFreeObject{ name, state }
+  }
+
+  transition () {
+    \ state.addOperation<#self>(name)
+    \ state.kill()
+    return DropSharedCheckWeak.new(name,state)
+  }
+}
+
+
+concrete DropSharedCheckWeak {
+  @type new (String,ReferenceState) -> (StateMachine)
+}
+
+define DropSharedCheckWeak {
+  $ReadOnly[name,state]$
+
+  refines StateMachine
+
+  @value String name
+  @value ReferenceState state
+
+  new (name,state) {
+    return DropSharedCheckWeak{ name, state }
+  }
+
+  transition () {
+    \ state.addOperation<#self>(name)
+    if (state.remWeak() == 0) {
+      return DropSharedFreeWeak.new(name,state)
+    } else {
+      return empty
+    }
+  }
+}
+
+
+concrete DropSharedFreeWeak {
+  @type new (String,ReferenceState) -> (StateMachine)
+}
+
+define DropSharedFreeWeak {
+  $ReadOnly[name,state]$
+
+  refines StateMachine
+
+  @value String name
+  @value ReferenceState state
+
+  new (name,state) {
+    return DropSharedFreeWeak{ name, state }
+  }
+
+  transition () {
+    \ state.addOperation<#self>(name)
+    \ state.cleanupWeak()
+    return empty
+  }
+}
diff --git a/tests/simulate-refs/state.0rp b/tests/simulate-refs/state.0rp
new file mode 100644
--- /dev/null
+++ b/tests/simulate-refs/state.0rp
@@ -0,0 +1,27 @@
+/* -----------------------------------------------------------------------------
+Copyright 2021 Kevin P. Barry
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+----------------------------------------------------------------------------- */
+
+// Author: Kevin P. Barry [ta0kira@gmail.com]
+
+$ModuleOnly$
+
+@value interface StateMachine {
+  transition () -> (optional StateMachine)
+}
+
+concrete StateExecutor {
+  @category multiplexStates (DefaultOrder<StateMachine>,Generator<Float>) -> ()
+}
diff --git a/tests/simulate-refs/state.0rx b/tests/simulate-refs/state.0rx
new file mode 100644
--- /dev/null
+++ b/tests/simulate-refs/state.0rx
@@ -0,0 +1,46 @@
+/* -----------------------------------------------------------------------------
+Copyright 2021 Kevin P. Barry
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+----------------------------------------------------------------------------- */
+
+// Author: Kevin P. Barry [ta0kira@gmail.com]
+
+define StateExecutor {
+  multiplexStates (original,random) {
+    SearchTree<Int,StateMachine> states <- SearchTree<Int,StateMachine>.new()
+    CategoricalTree<Int> weights <- CategoricalTree<Int>.new()
+
+    scoped {
+      Int index <- 0
+    } in traverse (original.defaultOrder() -> StateMachine state) {
+      \ states.set(index,state)
+      \ weights.setWeight(index,1)
+    } update {
+      index <- index+1
+    }
+    $Hidden[original]$
+
+    while (weights.getTotal() > 0) {
+      Int index <- weights.locate((random.generate()*weights.getTotal().asFloat()).asInt())
+      scoped {
+        optional StateMachine newState <- require(states.get(index)).transition()
+      } in if (present(newState)) {
+        \ states.set(index,require(newState))
+      } else {
+        \ states.remove(index)
+        \ weights.setWeight(index,0)
+      }
+    }
+  }
+}
diff --git a/tests/simulate-refs/weak.0rp b/tests/simulate-refs/weak.0rp
new file mode 100644
--- /dev/null
+++ b/tests/simulate-refs/weak.0rp
@@ -0,0 +1,23 @@
+/* -----------------------------------------------------------------------------
+Copyright 2021 Kevin P. Barry
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+----------------------------------------------------------------------------- */
+
+// Author: Kevin P. Barry [ta0kira@gmail.com]
+
+$ModuleOnly$
+
+concrete WeakRoutine {
+  @type new (String,ReferenceState) -> (StateMachine)
+}
diff --git a/tests/simulate-refs/weak.0rx b/tests/simulate-refs/weak.0rx
new file mode 100644
--- /dev/null
+++ b/tests/simulate-refs/weak.0rx
@@ -0,0 +1,174 @@
+/* -----------------------------------------------------------------------------
+Copyright 2021 Kevin P. Barry
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+----------------------------------------------------------------------------- */
+
+// Author: Kevin P. Barry [ta0kira@gmail.com]
+
+define WeakRoutine {
+  new (name,state) {
+    \ state.addWeak()
+    return ToStrongEnter.new(name,state)
+  }
+}
+
+
+concrete ToStrongEnter {
+  @type new (String,ReferenceState) -> (StateMachine)
+}
+
+define ToStrongEnter {
+  $ReadOnly[name,state]$
+
+  refines StateMachine
+
+  @value String name
+  @value ReferenceState state
+
+  new (name,state) {
+    return ToStrongEnter{ name, state }
+  }
+
+  transition () {
+    \ state.addOperation<#self>(name)
+    if (state.addLock() % ReferenceState.lockMod() == 0) {
+      return ToStrongDead.new(name,state)
+    } else {
+      return ToStrongAlive.new(name,state)
+    }
+  }
+}
+
+
+concrete ToStrongAlive {
+  @type new (String,ReferenceState) -> (StateMachine)
+}
+
+define ToStrongAlive {
+  $ReadOnly[name,state]$
+
+  refines StateMachine
+
+  @value String name
+  @value ReferenceState state
+
+  new (name,state) {
+    return ToStrongAlive{ name, state }
+  }
+
+  transition () {
+    \ state.addOperation<#self>(name)
+    \ state.convertLock()
+    return ToStrongDiscardWeak.new(name,state)
+  }
+}
+
+
+concrete ToStrongDiscardWeak {
+  @type new (String,ReferenceState) -> (StateMachine)
+}
+
+define ToStrongDiscardWeak {
+  $ReadOnly[name,state]$
+
+  refines StateMachine
+
+  @value String name
+  @value ReferenceState state
+
+  new (name,state) {
+    return ToStrongDiscardWeak{ name, state }
+  }
+
+  transition () {
+    \ state.addOperation<#self>(name)
+    \ state.remWeak()
+    return SharedRoutine.alreadyStrong(name,state)
+  }
+}
+
+
+concrete ToStrongDead {
+  @type new (String,ReferenceState) -> (StateMachine)
+}
+
+define ToStrongDead {
+  $ReadOnly[name,state]$
+
+  refines StateMachine
+
+  @value String name
+  @value ReferenceState state
+
+  new (name,state) {
+    return ToStrongDead{ name, state }
+  }
+
+  transition () {
+    \ state.addOperation<#self>(name)
+    \ state.remLock()
+    return ToStrongCheckWeak.new(name,state)
+  }
+}
+
+
+concrete ToStrongCheckWeak {
+  @type new (String,ReferenceState) -> (StateMachine)
+}
+
+define ToStrongCheckWeak {
+  $ReadOnly[name,state]$
+
+  refines StateMachine
+
+  @value String name
+  @value ReferenceState state
+
+  new (name,state) {
+    return ToStrongCheckWeak{ name, state }
+  }
+
+  transition () {
+    \ state.addOperation<#self>(name)
+    if (state.remWeak() == 0) {
+      return ToStrongFreeWeak.new(name,state)
+    } else {
+      return empty
+    }
+  }
+}
+
+
+concrete ToStrongFreeWeak {
+  @type new (String,ReferenceState) -> (StateMachine)
+}
+
+define ToStrongFreeWeak {
+  $ReadOnly[name,state]$
+
+  refines StateMachine
+
+  @value String name
+  @value ReferenceState state
+
+  new (name,state) {
+    return ToStrongFreeWeak{ name, state }
+  }
+
+  transition () {
+    \ state.addOperation<#self>(name)
+    \ state.cleanupWeak()
+    return empty
+  }
+}
diff --git a/tests/traverse.0rt b/tests/traverse.0rt
--- a/tests/traverse.0rt
+++ b/tests/traverse.0rt
@@ -68,6 +68,18 @@
   \ Testing.checkEquals<?>(total,20)
 }
 
+unittest withUpdate {
+  Int total <- 0
+  traverse (Counter.zeroIndexed(10) -> Int i) {
+    if (i%2 == 1) {
+      continue
+    }
+  } update {
+    total <- total+i
+  }
+  \ Testing.checkEquals<?>(total,20)
+}
+
 concrete Counter {
   refines Order<Int>
 
diff --git a/zeolite-lang.cabal b/zeolite-lang.cabal
--- a/zeolite-lang.cabal
+++ b/zeolite-lang.cabal
@@ -1,7 +1,7 @@
 cabal-version:       2.2
 
 name:                zeolite-lang
-version:             0.18.1.0
+version:             0.19.0.0
 synopsis:            Zeolite is a statically-typed, general-purpose programming language.
 
 description:
@@ -25,7 +25,14 @@
     automatically build the supporting libraries.
     .
     @
+    # By default, setup is interactive.
     zeolite-setup
+    .
+    # To choose the c++ and ar binaries non-interactively, include them as args.
+    zeolite-setup \/usr\/bin\/clang++ \/usr\/bin\/ar
+    .
+    # Or just choose the 1st match for each.
+    zeolite-setup 1 1
     @
     .
   * (Optional) As a sanity check, compile and run
@@ -84,6 +91,9 @@
                      example/primes/README.md,
                      example/primes/*.0rp,
                      example/primes/*.0rx,
+                     example/random/.zeolite-module,
+                     example/random/README.md,
+                     example/random/*.0rx,
                      lib/container/README.md,
                      lib/container/.zeolite-module,
                      lib/container/*.0rp,
@@ -152,7 +162,12 @@
                      tests/module-only4/.zeolite-module,
                      tests/module-only4/*.cpp,
                      tests/module-only4/*.0rp,
+                     tests/module-only4/*.0rt,
                      tests/module-only4/*.0rx,
+                     tests/simulate-refs/.zeolite-module,
+                     tests/simulate-refs/*.0rp,
+                     tests/simulate-refs/*.0rx,
+                     tests/simulate-refs/README.md,
                      tests/templates/README.md,
                      tests/templates/.zeolite-module,
                      tests/templates/*.0rp,
