How to tell function and generator function apart?

classic Classic list List threaded Threaded
13 messages Options
Reply | Threaded
Open this post in threaded view
|

How to tell function and generator function apart?

Guilherme Souza
Hi all,

I was wondering how one could check if a given function is a generator function, is a cross-realm way:

```
let f = function(){}
let g = function*(){}

typeof f === typeof g // 'function'

f instanceof Function // true, but only in the same Realm
g instanceof Function // true, but again only in the same Realm

let GeneratorFunction = Object.getPrototypeOf(function*(){}).constructor
f instanceof GeneratorFunction // false
g instanceof GeneratorFunction // true, but only in the same Realm

// This works, but relies on Function.prototype being a function itself.
typeof Object.getPrototypeOf(f) // 'function'
typeof Object.getPrototypeOf(g) // 'object'
```

So what is the recommended way to do this type of check?
Would something like `Array.isArray` for generator functions be helpful?

Regards,
Gui



_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: How to tell function and generator function apart?

Claude Pache
Here is how to get your answer:

g.toString().startsWith("function*")

Since being a generator function (in contrast with being a function that may return an iterator) is a question of implementation rather of behaviour, I think that peeking at the source code using `.toString()` is the right way to do it.

—Claude

Le 3 mars 2015 à 17:41, Guilherme Souza <[hidden email]> a écrit :

Hi all,

I was wondering how one could check if a given function is a generator function, is a cross-realm way:

```
let f = function(){}
let g = function*(){}

typeof f === typeof g // 'function'

f instanceof Function // true, but only in the same Realm
g instanceof Function // true, but again only in the same Realm

let GeneratorFunction = Object.getPrototypeOf(function*(){}).constructor
f instanceof GeneratorFunction // false
g instanceof GeneratorFunction // true, but only in the same Realm

// This works, but relies on Function.prototype being a function itself.
typeof Object.getPrototypeOf(f) // 'function'
typeof Object.getPrototypeOf(g) // 'object'
```

So what is the recommended way to do this type of check?
Would something like `Array.isArray` for generator functions be helpful?

Regards,
Gui


_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss


_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: How to tell function and generator function apart?

Damian Senn
In reply to this post by Guilherme Souza
Hi Gui

On 03/03/2015 05:41 PM, Guilherme Souza wrote:
> Hi all,
>
> I was wondering how one could check if a given function is a generator
> function, is a cross-realm way:

I'm detecting this with the following piece of code:

   if ((function*(){}).constructor.name == 'GeneratorFunction')) {
     // is generator function
   }

or something like:

   function isGenerator(fn) {
     return fn && fn.constructor && fn.constructor.name ==
'GeneratorFunction'
   }

Regards
Damian

--
Adfinis SyGroup AG
Damian Senn, Software Engineer

Keltenstrasse 98 | CH-3018 Bern
Tel. 031 550 31 11

_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: How to tell function and generator function apart?

Jordan Harband
It's not quite that simple due to variations across different engines, but I've got a module https://www.npmjs.com/package/is-generator-function that covers it.

On Tue, Mar 3, 2015 at 9:07 AM, Damian Senn <[hidden email]> wrote:
Hi Gui

On 03/03/2015 05:41 PM, Guilherme Souza wrote:
Hi all,

I was wondering how one could check if a given function is a generator function, is a cross-realm way:

I'm detecting this with the following piece of code:

  if ((function*(){}).constructor.name == 'GeneratorFunction')) {
    // is generator function
  }

or something like:

  function isGenerator(fn) {
    return fn && fn.constructor && fn.constructor.name == 'GeneratorFunction'
  }

Regards
Damian

--
Adfinis SyGroup AG
Damian Senn, Software Engineer

Keltenstrasse 98 | CH-3018 Bern
Tel. 031 550 31 11


_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss


_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: How to tell function and generator function apart?

Erik Arvidsson
In reply to this post by Guilherme Souza
Why do you want to do this?

It is an antipattern that we have covered before. 

On Tue, Mar 3, 2015 at 5:41 PM Guilherme Souza <[hidden email]> wrote:
Hi all,

I was wondering how one could check if a given function is a generator function, is a cross-realm way:

```
let f = function(){}
let g = function*(){}

typeof f === typeof g // 'function'

f instanceof Function // true, but only in the same Realm
g instanceof Function // true, but again only in the same Realm

let GeneratorFunction = Object.getPrototypeOf(function*(){}).constructor
f instanceof GeneratorFunction // false
g instanceof GeneratorFunction // true, but only in the same Realm

// This works, but relies on Function.prototype being a function itself.
typeof Object.getPrototypeOf(f) // 'function'
typeof Object.getPrototypeOf(g) // 'object'
```

So what is the recommended way to do this type of check?
Would something like `Array.isArray` for generator functions be helpful?

Regards,
Gui


_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss

_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: How to tell function and generator function apart?

Dean Landolt
One use case is for coroutine runners which accept either generators or generator functions:


Is there a better way to accomplish this?


On Tue, Mar 3, 2015 at 12:12 PM, Erik Arvidsson <[hidden email]> wrote:
Why do you want to do this?

It is an antipattern that we have covered before. 

On Tue, Mar 3, 2015 at 5:41 PM Guilherme Souza <[hidden email]> wrote:
Hi all,

I was wondering how one could check if a given function is a generator function, is a cross-realm way:

```
let f = function(){}
let g = function*(){}

typeof f === typeof g // 'function'

f instanceof Function // true, but only in the same Realm
g instanceof Function // true, but again only in the same Realm

let GeneratorFunction = Object.getPrototypeOf(function*(){}).constructor
f instanceof GeneratorFunction // false
g instanceof GeneratorFunction // true, but only in the same Realm

// This works, but relies on Function.prototype being a function itself.
typeof Object.getPrototypeOf(f) // 'function'
typeof Object.getPrototypeOf(g) // 'object'
```

So what is the recommended way to do this type of check?
Would something like `Array.isArray` for generator functions be helpful?

Regards,
Gui


_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss

_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss



_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: How to tell function and generator function apart?

Guilherme Souza
My use case is similar to what Dean has showed:

My `run()` lib abstraction should work with:

* a function, with node's done() callback style
* a generator function, with co's yield promise style [1]

```
// ES5 function
run(function (data, done) {
    doSomeAsyncJob(data, done)
})

// Generator function
run(function* (data) {
    return yield getSomeAsyncJobPromise(data)
})

On Tue, Mar 3, 2015 at 5:54 PM, Dean Landolt <[hidden email]> wrote:
One use case is for coroutine runners which accept either generators or generator functions:


Is there a better way to accomplish this?


On Tue, Mar 3, 2015 at 12:12 PM, Erik Arvidsson <[hidden email]> wrote:
Why do you want to do this?

It is an antipattern that we have covered before. 

On Tue, Mar 3, 2015 at 5:41 PM Guilherme Souza <[hidden email]> wrote:
Hi all,

I was wondering how one could check if a given function is a generator function, is a cross-realm way:

```
let f = function(){}
let g = function*(){}

typeof f === typeof g // 'function'

f instanceof Function // true, but only in the same Realm
g instanceof Function // true, but again only in the same Realm

let GeneratorFunction = Object.getPrototypeOf(function*(){}).constructor
f instanceof GeneratorFunction // false
g instanceof GeneratorFunction // true, but only in the same Realm

// This works, but relies on Function.prototype being a function itself.
typeof Object.getPrototypeOf(f) // 'function'
typeof Object.getPrototypeOf(g) // 'object'
```

So what is the recommended way to do this type of check?
Would something like `Array.isArray` for generator functions be helpful?

Regards,
Gui


_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss

_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss





--
Graduando em Engenharia de Controle e Automação
Universidade Estadual de Campinas


_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

RE: How to tell function and generator function apart?

Domenic Denicola
As discussed previously such overloading is pretty bad. If someone creates a generator-returning function manually, instead of using a generator function to do so, they will start getting unexpected results from your library. You might think this is rare but it's actually pretty reasonable---consider e.g. `yourLibrary(transform(function* () { ... }))` where `transform` is a higher-order function which manually creates a generator based on its argument.

An analogy would be that your and Dean's functions are similar to something like

```js
function getFullName(person) {
  if (person.constructor !== Person) {
    // Do something else, e.g. `return person.toString()`
  }

  return `${person.firstName} ${person.lastName}`;
}
```

That is, now you have prohibited people from using { firstName, lastName } object literals, Person subclass instances, Dog instances, etc. That's very unfortunate practice. The same goes for generators.

_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: How to tell function and generator function apart?

Benjamin Gruenbaum-2
In reply to this post by Dean Landolt
Yes, don't make the distinction and instead run only promises - provide a function that takes a sequence and returns a promise over it. Not every iterable sequence is a sequence of async values.

On Mar 3, 2015, at 22:54, Dean Landolt <[hidden email]> wrote:

One use case is for coroutine runners which accept either generators or generator functions:


Is there a better way to accomplish this?


On Tue, Mar 3, 2015 at 12:12 PM, Erik Arvidsson <[hidden email]> wrote:
Why do you want to do this?

It is an antipattern that we have covered before. 

On Tue, Mar 3, 2015 at 5:41 PM Guilherme Souza <[hidden email]> wrote:
Hi all,

I was wondering how one could check if a given function is a generator function, is a cross-realm way:

```
let f = function(){}
let g = function*(){}

typeof f === typeof g // 'function'

f instanceof Function // true, but only in the same Realm
g instanceof Function // true, but again only in the same Realm

let GeneratorFunction = Object.getPrototypeOf(function*(){}).constructor
f instanceof GeneratorFunction // false
g instanceof GeneratorFunction // true, but only in the same Realm

// This works, but relies on Function.prototype being a function itself.
typeof Object.getPrototypeOf(f) // 'function'
typeof Object.getPrototypeOf(g) // 'object'
```

So what is the recommended way to do this type of check?
Would something like `Array.isArray` for generator functions be helpful?

Regards,
Gui


_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss

_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss



_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: How to tell function and generator function apart?

Dean Landolt
In reply to this post by Domenic Denicola
On Tue, Mar 3, 2015 at 5:26 PM, Domenic Denicola <[hidden email]> wrote:
As discussed previously such overloading is pretty bad. If someone creates a generator-returning function manually, instead of using a generator function to do so, they will start getting unexpected results from your library. You might think this is rare but it's actually pretty reasonable---consider e.g. `yourLibrary(transform(function* () { ... }))` where `transform` is a higher-order function which manually creates a generator based on its argument.

An analogy would be that your and Dean's functions are similar to something like

```js
function getFullName(person) {
  if (person.constructor !== Person) {
    // Do something else, e.g. `return person.toString()`
  }

  return `${person.firstName} ${person.lastName}`;
}
```

That is, now you have prohibited people from using { firstName, lastName } object literals, Person subclass instances, Dog instances, etc. That's very unfortunate practice. The same goes for generators.

 
FWIW the coroutine runner in my example is strictly a runner, not a wrapper returning something analogous to an async function. I definitely see the benefit of such a wrapper (and have been meaning to get around to adding one), but the example I'd linked to is more like running coroutine "program". I suspect there's still some benefit to this kind of style, if just for simple debug cases, but with the presence of some kind of async function wrapper, this "program" wrapper would really only need to support generator functions, which means no need to bother w/ any `isGeneratorFunction` nonsense.


_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

RE: How to tell function and generator function apart?

Domenic Denicola
That's not really the issue at hand, though. Your runner won't work with functions that manually return generator instances, due to its type-testing. Even if those functions return generator instances express a perfectly valid async program sequence.

_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: How to tell function and generator function apart?

Dean Landolt
On Tue, Mar 3, 2015 at 10:00 PM, Domenic Denicola <[hidden email]> wrote:
That's not really the issue at hand, though. Your runner won't work with functions that manually return generator instances, due to its type-testing. Even if those functions return generator instances express a perfectly valid async program sequence.


I don't follow -- it definitely used to work (I use the past tense because I went ahead and converted to do the async fn wrapper thing instead). The type test was just to determine whether or not to flatten generator functions into generator instances, which is what the coro trampoline expects. Any generator instance can be run through that coro tramp -- even ones not designed as coroutines (not recommended, of course -- though it'd be fine so long as it terminates).

I just noticed the coro tramp in the async/await spec: https://github.com/lukehoban/ecmascript-asyncawait#spawning. This is just about identical to my own, though I can't quite figure out why it bothers operating on generator functions rather than instances. It seems more natural to instantiate the generators (and their argument bindings) from within an async fn wrapper, e.g. https://github.com/deanlandolt/copromise/blob/master/copromise.js#L23.

Removing the type test definitely cleaned up the API, and managed to shrink an already tiny code base even more, so it was clearly a win. But I still can't see how how type testing would have broken anything at all in the previous design.

_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss
Reply | Threaded
Open this post in threaded view
|

Re: How to tell function and generator function apart?

Claude Pache
In reply to this post by Guilherme Souza

Le 3 mars 2015 à 23:19, Guilherme Souza <[hidden email]> a écrit :

My use case is similar to what Dean has showed:

My `run()` lib abstraction should work with:

* a function, with node's done() callback style
* a generator function, with co's yield promise style [1]

```
// ES5 function
run(function (data, done) {
    doSomeAsyncJob(data, done)
})

// Generator function
run(function* (data) {
    return yield getSomeAsyncJobPromise(data)
})


The hard part is that you should take the "generator function" path for functions that *behave* like generator functions even if they are not, e.g., `gen.bind(null, 'foo')`, where `gen` is a true generator function.

This is how I'd consider to do, if I *really* couldn't ask the user about the nature of the function.

1. At first, don't make any assumption about which code path to take. Let `f` be the function to work with;
2. As a common part of the algorithms for the two cases, call `f(data, done)`, assuming that it is equivalent to `f(data)` if `f` is a generator function;
3. Check whether the result is iterable, i.e, if `typeof result[Symbol.iterator] === 'function'`. If yes, proceed with the "generator function" path. If no, proceed with the "function-with-callback" path.

—Claude

_______________________________________________
es-discuss mailing list
[hidden email]
https://mail.mozilla.org/listinfo/es-discuss