Jump To …

syntax.js

guard :: Contract x any x String x String

functions: simple

return guard(
     fun(Str, Bool),
     function(s) { return false; },
     server, client)

multiple args

return guard(
     fun([Str, Bool], Bool),
     function(s, b) { return false; },
     server, client)

optional args

return guard(
     fun([Str, opt(Bool), opt(Str)], Bool),
     function(s, b) { /* ... */ },
     server, client)

optional args, bad form

return guard(
     fun([Str, opt(Bool), opt(Str), Num], Bool), // fail, can't have non-optional after optional
     function(s, b) { /* ... */ },
     server, client)

higher-order

return guard(
     fun(fun(Str, Bool), Str),
     function(f, b) { /* ... */ },
     server, client)

dependent

return guard(
     fun(Str, function(arg) { return check(function(r) { return arg === r; }); }),
     function(x) { return x; },
     server, client)

constructor only

return guard(
     ctor(Str, object({a: Str, b: Num})),
     function(s) { this.a = s; this.b = 42; },
     server, client)

desugars to

return guard(
     functionContract(Str, object({a: Str, b: Num}),
                     { newOnly: true }),
     function(s) { this.a = s; this.b = 42; },
     server, client)

call only

return guard(
     funCall(Str, Bool), // funNN? funNoNew? funCO? default behavior for fun? no convenience form?
     function(s) { /* ... */ },
     server, client)

so .newOnly(true).callOnly(true) -> throw exception desugars to

return guard(
     functionContract(Str, Bool, { callOnly: true }),
     function(s) { /* ... */ },
     server, client)

lazy new constructor

return guard(
     ctorSafe(Str, object({a: Str, b: Num})),
     function(s) { this.a = s; this.b = 42; },
     server, client)

will automatically create the right new: new f(..) works normally f(..) will create the appropriate "this" and apply it to f desugars to

return guard(
     functionContract(Str, object({a: Str, b: Num}), { safeNew: true }),
     function(s) { this.a = s; this.b = 42; },
     server, client)

different contract for call and new

return guard(
     ctor({
         call: [Str, Bool],
         new : [[Str, Num], object({a: Str, b: Num})]
     }),
     function(s) { /* ... */ },
     server, client)

explicit contract on this

return guard(
     fun(Str, Bool,
         { this: object({ a: Str, b: Num })}
        ),
     function(s) { return this.a + this.b; },
     server, client)

lazy vs strict checking of object properties? what about .onlyNew(true).thisContract({a: Str})? throw exception?

objects: data obj

return guard(
     object({
          a: Str,
          b: Bool
     }),
     {a: "foo", b: false},
     server, client);

nested data objects eager vs lazy checking?

return guard(
     object({
          a: Str,
          b: object({
               c: Bool,
               d: Num
          })
     }),
     {a: "foo", b: {c: false, d: 42}},
     server, client)

forzen

return guard(
     object({
          a: Str,
          b: Num
     }, { freeze: true }), 
     {a: "foo", b: 42},
     server, client)     

optional properties

return guard(
    object({
        a: opt(Str),
        b: Num
    }),
    { b: 42 },
    server, client);

immutable properties

return guard(
     object({
          a: [Str, {immutable: true}],
          b: Num
     }), 
     {a: "foo", b: 42},
     server, client)     

recursive

var o = { a : "hi", b: null}
o.b = o;

or

var o = rec(function(o) { return { a : "hi", b: o}; });
return guard(
     object({
         a: Num,
         b: self,
         c: fun(Num, self),
         d: object({ y: Str, z: self }) // this self is not the same as the others!
     }),
     o,
     server, client)

object with simple method

return guard(
     object({
          a: Str,
          m: fun(Str, Bool)
     }),
     {a: "foo", m: function(s) { /* ... */ }},
     server, client)

implicit this contract? "desugars" to:

return guard(
     object({
          a: Str,
          m: fun(Str, Bool, {
              this : object({a: Str, m: fun(Str, Bool).thisContract(...)})
          })
     }),
     {a: "foo", m: function(s) { /* ... */ }},
     server, client)

if object contract checking of this is eager then we could have a problem...module pattern where methods are really functions that don't refer to this should be fine with lazy checking though? if function never uses this, checking never happens. what if we had a function that used this in a generic way? wouldn't want any contract so be explicit about there not being a contract? .thisContract({}) a little non-obvious but this seems like a rare pattern so probably fine?

object with simple method explicit about this contract

return guard(
     object({
          a: Str,
          m: fun(Str, Bool, { this: object({c: Bool, d: Str})})
     }),
     {a: "foo", m: function(s) { return this.c + this.d; }},
     server, client)

overrides the implicit this contract on methods

object with method that has pre/post conditions

return guard(
     object({
          a: Str,
          m: fun(Str, Bool, {
              pre: function(obj) { return obj.a === "foo"; }, // obj is a ref to the calling object (could be this?)
              post: function(obj) { return obj.a === "foo"; }
          })
     }),
     {a: "foo", m: function(s) { /* ... */ }},
     server, client)

prototype

var p = guard(
     object({
          a: Str,
          b: Num
     }),
     {a: "foo", b: 42},
     server, client)
var op = Object.create(p, {c: false, d: function() {}})
return guard(
     object({
          c: Bool,
          d: fun(Str, Bool)
     }),
     op,
     server, client)

anything weird going on here? blame goes to proto if failing proto contract. blame falls on op if fails op contract. all should be fine right?

maybe we want ability to distinguish between object and proxy in one swoop?

var op = Object.create({a: "foo", b: 42}, {c: false, d: function() {}})
return guard(
     object({
          c: Bool,
          d: fun(Str, Bool)
     }, {
         proto: object({     // this would be anywhere on the prototype chain (aka anything not ownProperty)
          a: Str,
          b: Num
         })}
     ),
     op,
     server, client)

does this really give us anything over the previous form?

List

return guard(
     List,
     [1,2,3],
     server, client)

desugars to

return guard(
     object({}, {
         frozen: true,
         noDelete: true,
         init: [Array.isArray, hasNoHoles]
     }),
     [1,2,3],
     server, client)

SaneArray

return guard(
     SaneArray,
     [1,2,3],
     server, client)

desugars to

return guard(
     object({}, {
          frozen : false,
          noDelete: true, // does this make sense? maybe only noDelete for indexes?
          init : [Array.isArray, hasNoHoles]
     }),
     [1,2,3],
     server, client)

array

return guard(
     Array,
     [1,2,3],
     server, client)

desugars to

return guard(
     object({}, {
          frozen : false,
          noDelete : false,
          init : [Array.isArray]
     }),
     [1,2,3],
     server, client)

arr is the structural version of array checking

return guard(
    arr([Str, Bool]), // or arr(Str, Bool)?
    ["foo", false],
    server, client
);

return guard(
    arr([___(Num)]),
    [1,2,3,4,5],
    server, client
);

return guard(
    arr([Str, ___(Num)]),
    ["foo", 1,2,3,4],
    server, client
);

return guard(
    arr([Str, ___(Num), Bool]),
    ["foo", 1,2,3,4, false],
    server, client
);

return guard(
    arr([_, ___(Num)]), // single underscore aliased to Any?
    [false, 1,2,3],
    server, client
);