3

I just got in JavaScript and noticed that lazy evaluation is not directly supported in this language. Natively the code turns into the hell of boiler plate like this:

function lazy(f) {
    var v = undefined;
    return function() {
        if (v == undefined) 
            v = f();
        return v;
    }
}

// 10 times larger than actual operation
var foo = lazy(function() {
    return 3 + 3;
});

But I found Sweet.js and believe that it can make the code simple like this:

var foo = lazy (3 + 3);

var goo = lazy {
    var a = 3 + 3;
    return a;
};

So I tested out Edit Sweet.js:

function lazy_f(f) {
    var v = undefined;
    return function() {
        if (v == undefined) 
            v = f();
        return v;
    }
}

macro lazy {
    rules { $expr } => {
        lazy_f(function() { return $expr; })
    }
}

var foo = lazy (3 + 3);

It worked with a single expr. But there are cases that lazy takes in a block of expr like this:

var goo = lazy {
    var a = 3 + 3;
    return a;
};

So I arranged the above code like this:

function lazy_f(f) {
    var v = undefined;
    return function() {
        if (v == undefined) 
            v = f();
        return v;
    }
}

macro lazy {
    rule { $($expr) (;) ... } => {     //
        lazy_f(function() $expr ...);  //
    }                                  //
    rules { $expr } => {
        lazy_f(function() { return $expr; })
    }
}

var foo = lazy (3 + 3);

var goo = lazy {
    var a = 3 + 3;
    return a;
};

And it doesn't work for some reason. I believe the first pattern $($expr) (;) ... shouldn't match with (3 + 3), but apparently it's doing.

I worked on this for an hour and finally gave up. How do you make the two patterns working at the same time?

If it's not possible to do so, I would like to take another way for a single expr:

lar foo = 3 + 3;
var foo_content = foo();

And I don't know how to do this as well.

Ryoichiro Oka
  • 1,949
  • 2
  • 13
  • 20

2 Answers2

3

$($expr) (;) ... means repeatedly match single tokens separated by ;. You probably want this:

macro lazy {
    // match single expression in parens
    rule { ( $e:expr ) } => {
        lazy_f(function() { return $e })
    }
    // match all of the tokens inside a curly
    rule { { $expr ... } } => {
        lazy_f(function() { $expr ... })
    }
}
timdisney
  • 5,287
  • 9
  • 35
  • 31
3

Since you want to use it with curly brackets, you need to include the brackets in the macro, like @timdisney said.

However, to lazily evaluate an expression like var a = lazy this.x * this.y, you need to properly bind the expression to the current value of this.

Full code

function lazyEvaluate(thisObject, functionToEvaluate) {
    var result, hasResult = false;

    return function () {
        if (hasResult) {
            return result;
        } else {
            hasResult = true;

            return result = functionToEvaluate.call(thisObject);
        };
    };
}

let lazy = macro {
    rule {
        {
            $statements ...
        }
    } => {
        lazyEvaluate(this, function () {
            $statements ...
        })
    }
    rule { $expression:expr } => {
        lazyEvaluate(this, function () {
            return $expression;
        })
    }
}

This should work for any valid expression.

Example

var a = 5, b = 2, c = lazy a * b, d = lazy {
    console.log(c());

    return c() * a;
};

a = 10;

console.log(c()); // 20

a = 5;

console.log(d()); // 100
Sean Vieira
  • 155,703
  • 32
  • 311
  • 293
Toothbrush
  • 2,080
  • 24
  • 33