Guards

Peter Michaux petermichaux at gmail.com
Fri May 27 10:59:40 PDT 2011


On Fri, May 27, 2011 at 1:44 AM, Brendan Eich <brendan at mozilla.com> wrote:
> On May 26, 2011, at 9:36 PM, Peter Michaux wrote:
>
> > I'm looking at the strawman for guards.
> >
> > http://wiki.ecmascript.org/doku.php?id=strawman:guards
> >
> > I don't quite see how they are created.
> >
> > I understand this part about how they would be used
> >
> >  function f(p :: String, q :: MyType) :: Boolean { ... }
> >
> > but there is no specific example of how MyType is defined. Is that
> > related to classes at all? I'm hoping it is not related to classes and
> > that it is more akin to some kind of interface definition.
>
> From http://wiki.ecmascript.org/doku.php?id=strawman:guards#creating_guards,
> "Optionally we may have an Object.setBrand(guard, brand) method that creates
> a [[Brand]] internal property on an extensible object guard that doesn’t
> already have one. It is an error to use Object.setBrand on a guard object
> that already has a [[Brand]] internal property."

I did see this when reading the strawman. It seems I missed the following bit

> The brand object (the value of the [[Brand]] internal property) can
> be anything. To be useful, it should be an object with a coerce method.

So would the following code be possible? (I've added Object.hasBrand(o).)

    var positiveNumberGuard = {};
    Object.setBrand(positiveNumberGuard, {
        coerce: function(specimen) {
            if (typeof specimen !== 'number') {
                throw new Error('must be a number');
            }
            if (specimen <= 0) {
                throw new Error('must be positive');
            }
            return specimen;
        }
    });

    var nonEmptyStringGuard = {};
    Object.setBrand(nonEmptyStringGuard, {
        coerce: function(specimen) {
            if (typeof specimen !== 'string') {
                throw new Error('must be a string');
            }
            if (specimen.length <= 0) {
                throw new Error('must be non-empty');
            }
            return specimen;
        }
    });

    var identityGuard = {};
    Object.setBrand(identityGuard, {
        coerce: function(specimen) {
            return specimen;
        }
    });

    var guardGuard = {};
    Object.setBrand(guardGuard {
        coerce: function(specimen) {
            if (!Object.hasBrand(specimen)) {
                throw new Error('must be a guard');
            }
            return specimen;
        }
    });

    var makeDoubler = function(guard = identityGuard :: guardGuard) {
        return function(x :: guard) {
            return x + x;
        };
    };

    var positiveNumberDoubler = makeDoubler(positiveNumberGuard);
    positiveNumberDoubler(1); // 2
    positiveNumberDoubler("a"); // throws "must be a number"

    var nonEmptyStringDoubler = makeDoubler(nonEmptyStringGuard);
    nonEmptyStringDoubler(1); // throws "must be a string"
    nonEmptyStringDoubler("a"); // "aa"

    var doubler = makeDoubler();
    doubler(1); // 2
    doubler("a"); // "aa"


I'm impressed with the following if this is what would actually be possible...

  function(guard = identityGuard :: guardGuard)

What we have to write now in ES3 can be so long to achieve this kind
of default value and parameter checking functionality.

--

The spec also has a bit about the return value of coerce.

> Call brand.coerce(s). This will either return a value (either s or
> possibly a coerced version of s) or throw an exception.

So the return value is actually what the parameter is set to before
the function body executes in the case of a guarded parameter?


> > Suppose I
> > wanted MyType to be only positive even integers. Perhaps MyType is a
> > can only be a function that takes a single argument that is a string
> > and returns an integer. Perhaps MyType has to be an object with two
> > function properties named alpha and beta.
>
> Exactly! Some in TC39 want to research contract systems for JS.

This is an alternate proposal to the guards, correct? I don't see
anything in the wiki index about contracts.


> We have a
> research intern at Mozilla building such a prototype this summer. The plan
> was to build a library without syntax, but Waldemar's proposal would give
> nice syntax (we think -- details to hash out of course) for this work.

I'd be interested in such a library. Is there any more information
what it might look like to use it?


I sort of hacked at one myself to do type checks but my work wasn't
very compelling.

function foo(a, b) {
    assertNumber(a, 'some optional message');
    assertString(a, 'some optional message');
    // body of function
};

The throw was coming from the assert function rather than the foo
function itself which was not so great.


> > What about maybe types? Could MyType be defined so that null is not an
> > allowed value? All the manually written not-null parameter checks in
> > Java are an unfortunate result of its type checking system.
>
> A static type system could perhaps be built on top of Harmony modules.

Just in case, I didn't mean to imply a static type system.

> Sam
> Tobin-Hochstadt's Typed Racket work seems helpful here, although it uses
> contracts too. This is all good research work.
> The point is it is research, not ready to be standardized.
>
> > One use case I could see would be using guards in development and then
> > having the minifier strip the guards for production. Then the guard
> > definitions could be stripped as well.
>
> As runtime checkers, you can't erase guards.

A minifier could, couldn't it?

function foo(a::MyType) {}

would be minified to

function foo(a) {}

Though with the example I gave above of the makeDoubler function this
minification strategy would likely fall apart.

Also if the guard can coerce that could be an important part of a
function's computation which gives me a bit of an uneasy feeling like
before filters do in aspect-oriented programming.

Peter


More information about the es-discuss mailing list