method JS::Root::arity(Code $self:) {
  JS::inline('new PIL2JS.Box.Constant(function (args) {
    args.pop()(new PIL2JS.Box.Constant(args[1].FETCH().pil2js_arity));
  })')($self);
}

method name(Code $self:) {
  JS::inline('new PIL2JS.Box.Constant(function (args) {
    args.pop()(new PIL2JS.Box.Constant(args[1].FETCH().pil2js_name));
  })')($self);
}

# XXX waiting for luqui's tuple type:
# method assuming(Code $self: *$args) {
#   return sub (*$new_args) {
#     $self(*$args, *$new_args);
#   };
# }
method assuming(Code $self: *@args) {
  return sub (*@new_args) {
    $self([,] @args, @new_args);
  };
}

sub lazy (Code $fetch) is primitive {
  JS::inline('new PIL2JS.Box.Constant(function (args) {
    var fetch = args[1].FETCH(), cc = args.pop();

    // Firstly, we create a boxed undefined. This will later become our proxy.
    var boxed = new PIL2JS.Box(undefined);

    // Then we can setup the reference.
    var ref       = new PIL2JS.Ref(boxed);
    ref.autoderef = true;
    ref           = new PIL2JS.Box(ref);

    // Finally we fix boxed.FETCH and boxed.STORE.
    // We had to delay this because PIL2JS.Ref calls .FETCH() (to init its
    // .container_type), but this is clearly not what we want (remember,
    // we\'re implementing *lazy*).
    var cached_result;
    var already_calced = false;

    boxed.FETCH  = function ()  {
      if(!already_calced) {
        cached_result = PIL2JS.cps2normal(
          fetch,
          [PIL2JS.Context.ItemAny]
        ).FETCH();
        already_calced = true;
      }

      return cached_result;
    };

    boxed.STORE  = function (n) { PIL2JS.die("Can\'t modify lazy {...} value!") };
    boxed.BINDTO = function (n) { PIL2JS.die("Can\'t rebind lazy {...} value!") };
    ref.STORE    = function (n) { PIL2JS.die("Can\'t modify lazy {...} value!") };
    ref.BINDTO   = function (n) { PIL2JS.die("Can\'t rebind lazy {...} value!") };

    cc(ref);
  })')($fetch);
}
