New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Request: Basic lazy evaluation. #46

Closed
thomasfoster96 opened this Issue Jun 15, 2015 · 9 comments

Comments

Projects
None yet
5 participants
@thomasfoster96

thomasfoster96 commented Jun 15, 2015

Sorry if this is the wrong place to post this (or if someone has already proposed it).

Lately I've been writing quite a bit of code which needs some things to be lazily evaluated. So far I've just been using functions with empty parameters, but having lots of empty function parameters all over the place makes things unnecessarily verbose and hard to read. Some examples of where this might happen:

// Logging a value after a specified amount of time.
setTimeout(function(){console.log("Ten seconds have passed")},10000);
setTimeout(()=>console.log("Ten seconds have passed"),10000);

// Generic library that lazily evaluates chained methods.
bigArray.map(x=x*2).filter(x=>x>10).sort((a,b)=>a-b).exec()

As you can see, a function expression with empty parameters has 10 unnecessary characters, while an arrow function with empty parameters has 4 unnecessary characters.

My first thought was to wrap expressions to be lazily evaluated in backticks, but they are now used for template strings. Perhaps putting a @ or a # before an expression wrapped in parentheses or statement wrapped in curly braces would be acceptable?

As for evaluating the expression/statement, I'd think that expression() would make sense. This would mean that if the chained methods example above was used, then the .exec() or .done() method could just be (). The examples above might then become:

setTimeout(@(console.log("Ten seconds have passed"), 10000);

bigArray.map(x=x*2).filter(x=>x>10).sort((a,b)=>a-b)()
@bterlson

This comment has been minimized.

Show comment
Hide comment
@bterlson

bterlson Jun 15, 2015

Member

Headless arrows, eg. => console.log("foo"), might be good enough? They were considered for ES6 but were somewhat contentious.

Member

bterlson commented Jun 15, 2015

Headless arrows, eg. => console.log("foo"), might be good enough? They were considered for ES6 but were somewhat contentious.

@thomasfoster96

This comment has been minimized.

Show comment
Hide comment
@thomasfoster96

thomasfoster96 Jun 16, 2015

They could work quite well.

The @ is probably a no go because of decorators.

thomasfoster96 commented Jun 16, 2015

They could work quite well.

The @ is probably a no go because of decorators.

@jamiebuilds

This comment has been minimized.

Show comment
Hide comment
@jamiebuilds

jamiebuilds Jun 16, 2015

Member

I spoke to @wycats awhile back about something along the lines of this:

let arr = [1...10];
let result = arr.entries()->filter(isEven)->drop(2)->take(2);

console.log(result); // [6, 8]

Note: The -> is really just a placeholder for whatever the syntax might be.

Desugared:

let arr = [1...10];
let iter = take(drop(filter(arr.entries(), isEven), 2), 2);
let result = [];

for (let entry of iter) {
  result.push(entry[1]);
}

console.log(result); // [6, 8]

The methods would look like this:

function* filter(iterator, callbackFn, thisArg) {
  let index = 0;
  for (let entry of iterator) {
    if (callbackFn.call(thisArg, entry[1], entry[0])) {
      yield [index++, entry[1]];
    }
  }
}

function* reject(iterator, callbackFn, thisArg) {
  let index = 0;
  for (let entry of iterator) {
    if (callbackFn.call(thisArg, entry[1], entry[0])) {
      yield [index++, entry[1]];
    }
  }
}

function* drop(iterator, n=1) {
  let index = 0;
  for (let entry of iterator) {
    if (entry[0] >= n) {
      yield [index++, entry[1]];
    }
  }
}

function* take(iterator, n=1) {
  let index = 0;
  for (let entry of iterator) {
    if (entry[0] < n) {
      yield [index++, entry[1]];
    }
  }
}

Demo

Member

jamiebuilds commented Jun 16, 2015

I spoke to @wycats awhile back about something along the lines of this:

let arr = [1...10];
let result = arr.entries()->filter(isEven)->drop(2)->take(2);

console.log(result); // [6, 8]

Note: The -> is really just a placeholder for whatever the syntax might be.

Desugared:

let arr = [1...10];
let iter = take(drop(filter(arr.entries(), isEven), 2), 2);
let result = [];

for (let entry of iter) {
  result.push(entry[1]);
}

console.log(result); // [6, 8]

The methods would look like this:

function* filter(iterator, callbackFn, thisArg) {
  let index = 0;
  for (let entry of iterator) {
    if (callbackFn.call(thisArg, entry[1], entry[0])) {
      yield [index++, entry[1]];
    }
  }
}

function* reject(iterator, callbackFn, thisArg) {
  let index = 0;
  for (let entry of iterator) {
    if (callbackFn.call(thisArg, entry[1], entry[0])) {
      yield [index++, entry[1]];
    }
  }
}

function* drop(iterator, n=1) {
  let index = 0;
  for (let entry of iterator) {
    if (entry[0] >= n) {
      yield [index++, entry[1]];
    }
  }
}

function* take(iterator, n=1) {
  let index = 0;
  for (let entry of iterator) {
    if (entry[0] < n) {
      yield [index++, entry[1]];
    }
  }
}

Demo

@thomasfoster96

This comment has been minimized.

Show comment
Hide comment
@thomasfoster96

thomasfoster96 Jun 16, 2015

@thejameskyle Yeah I don't think -> would work as final syntax because AFAIK that's also the (unofficially?) proposed syntax for another type of arrow functions.

Though your example does cover something I hadn't thought about - adding lazy evaluation to existing methods.

thomasfoster96 commented Jun 16, 2015

@thejameskyle Yeah I don't think -> would work as final syntax because AFAIK that's also the (unofficially?) proposed syntax for another type of arrow functions.

Though your example does cover something I hadn't thought about - adding lazy evaluation to existing methods.

@jamiebuilds

This comment has been minimized.

Show comment
Hide comment
@jamiebuilds

jamiebuilds Jun 16, 2015

Member

Yeah I don't think -> would work as final syntax because AFAIK that's also the (unofficially?) proposed syntax for another type of arrow functions.

Yeah like I said, just a placeholder instead of a .. I haven't seen any other proposals though, link?

Though your example does cover something I hadn't thought about - adding lazy evaluation to existing methods.

Yeah, and how to extend that, at first I was think of property lookups, however that's not really extensible without overriding prototypes.

arr.entries()->filter() // Array.prototype.filter
// vs
function* filter() {}
arr.entries()->filter(isEven);

I was also thinking about the new function bind proposal and wondering if lazy evaluation could be implemented for generators. /cc @zenparsing

let arr = [1, 2];

arr.entries()::filter(a)::forEach(b)
// a(1), a(2), b(1), b(2)

arr.entries()::filterGenerator(a)::forEachGenerator(b)
// a(1), b(1), a(2), b(2)

arr.entries()::filterGenerator(a)::forEach(b)
// a(1), a(2), b(1), b(2)
Member

jamiebuilds commented Jun 16, 2015

Yeah I don't think -> would work as final syntax because AFAIK that's also the (unofficially?) proposed syntax for another type of arrow functions.

Yeah like I said, just a placeholder instead of a .. I haven't seen any other proposals though, link?

Though your example does cover something I hadn't thought about - adding lazy evaluation to existing methods.

Yeah, and how to extend that, at first I was think of property lookups, however that's not really extensible without overriding prototypes.

arr.entries()->filter() // Array.prototype.filter
// vs
function* filter() {}
arr.entries()->filter(isEven);

I was also thinking about the new function bind proposal and wondering if lazy evaluation could be implemented for generators. /cc @zenparsing

let arr = [1, 2];

arr.entries()::filter(a)::forEach(b)
// a(1), a(2), b(1), b(2)

arr.entries()::filterGenerator(a)::forEachGenerator(b)
// a(1), b(1), a(2), b(2)

arr.entries()::filterGenerator(a)::forEach(b)
// a(1), a(2), b(1), b(2)
@benjamingr

This comment has been minimized.

Show comment
Hide comment
@benjamingr

benjamingr Jun 17, 2015

Contributor

@thejameskyle yes, lazy iteration over iterables is something that will probably be adopted, there are already libraries like lazy.js and kris kowal wrote a library that does this (with the iteration protocol) over 2 years ago (I think?) - nothing new about lazy sequences :)

Contributor

benjamingr commented Jun 17, 2015

@thejameskyle yes, lazy iteration over iterables is something that will probably be adopted, there are already libraries like lazy.js and kris kowal wrote a library that does this (with the iteration protocol) over 2 years ago (I think?) - nothing new about lazy sequences :)

@thomasfoster96

This comment has been minimized.

Show comment
Hide comment
@thomasfoster96

thomasfoster96 Aug 4, 2015

@thejameskyle Having had a second look at your use case, wouldn't it just be better to add .filter, .map and .forEach to generators? I think something similar is proposed for Observable.

thomasfoster96 commented Aug 4, 2015

@thejameskyle Having had a second look at your use case, wouldn't it just be better to add .filter, .map and .forEach to generators? I think something similar is proposed for Observable.

@bterlson

This comment has been minimized.

Show comment
Hide comment
@bterlson

bterlson Sep 22, 2015

Member

Closing this issue now. Feature requests should be made on es-discuss.

Member

bterlson commented Sep 22, 2015

Closing this issue now. Feature requests should be made on es-discuss.

@bterlson bterlson closed this Sep 22, 2015

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment