Skip to content
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

ES6 Generators #534

Closed
wants to merge 9 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 13 additions & 5 deletions lib/core.js
Original file line number Diff line number Diff line change
Expand Up @@ -300,13 +300,21 @@ interface IteratorResult<T> {
value?: T;
}

interface Iterator<T> {
next(): IteratorResult<T>;
@@iterator(): Iterator<T>;
interface $Iterator<Yield,Return,Next> {
@@iterator(): $Iterator<Yield,Return,Next>;
next(value?: Next): IteratorResult<Yield|Return>;
}
type Iterator<T> = $Iterator<T,void,void>;

interface Iterable<T> {
@@iterator(): Iterator<T>;
interface $Iterable<Yield,Return,Next> {
@@iterator(): $Iterator<Yield,Return,Next>;
}
type Iterable<T> = $Iterable<T,void,void>;

/* Generators */
interface Generator<Yield,Return,Next> {
@@iterator(): $Iterator<Yield,Return,Next>;
next(value?: Next): IteratorResult<Yield|Return>;
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Super rough idea, but I wonder if we could express this as something like

interface $Iterator<Y, R, N> {
  next(value?: N): IteratorResult<Y>;
}
type Iterator<T> = $Iterator<T, *, *>

interface $Iterable<Y, R, N> {
  @@iterator(): $Iterator<Y, R, N>;
}
type Iterable<T> = $Iterable<T, *, *>;

// Generator<Y, R, N> should be an $Iterable<Y, R, N>
// Generator<Y, R, N> should be an Iterable<Y>
// Generator<Y, R, N> should be an $Iterator<Y, R, N>
// Generator<Y, R, N> should be an Iterator<Y>
interface Generator<Y, R, N> {
    @@iterator(): Iterator<Y>;
    next(value?: N): IteratorResult<Y|R>;
}

declare function $yieldDelegate<R, N>(g: $Iterable<*, R, N>, n:N): R

If we do this, I think we could deal with something like

yield *({ [Symbol.iterator]: function *() { return yield 4; } })

ignoring the fact that Flow doesn't have support for Symbol.iterator, of course :P

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I'm a bit unclear about what that buys us. Could you extend your example with a test case showing an expected type error?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So here's what I'm trying to do. If you yield * to a generator, your PR accurately tries to match the delegating generator's R and N to the delegated generator's R and N. This is right and correct and awesome.

If you're yield *'ing to something that is NOT a generator but IS an Iterable, then you're not doing anything with the delegating generator's R and N. This is fine if the Iterable is something like an array, since R is void and N is void. However, I can conceive of a custom Iterable who's Iterator is something fancy, like a generator.

So given the example above

yield *({ [Symbol.iterator]: function *() { return yield 4; } })

If you call next('batman'), this yield * expression should evaluate to batman. (The example in babel: https://goo.gl/1Xq5VV)

I think it's possible to capture this idea, by creating $Iterator and $Iterable which keep track of the N and R types too. I'm not possible it will work, but it would be cool!

/* Maps and Sets */
Expand Down
15 changes: 8 additions & 7 deletions src/typing/constraint_js.ml
Original file line number Diff line number Diff line change
Expand Up @@ -806,7 +806,8 @@ module Scope = struct
(* a var scope corresponds to a runtime activation,
e.g. a function. *)
type var_scope_attrs = {
async: bool
async: bool;
generator: bool
}

(* var and lexical scopes differ in hoisting behavior
Expand Down Expand Up @@ -836,11 +837,10 @@ module Scope = struct
refis = KeyMap.empty;
}

(* return a fresh scope of the most common kind (var, non-async) *)
let fresh () = fresh_impl (VarScope { async = false })

(* return a fresh async var scope *)
let fresh_async () = fresh_impl (VarScope { async = true })
(* return a fresh scope of the most common kind (var) *)
let fresh ?(async=false) ?(generator=false) () =
assert (not (async && generator));
fresh_impl (VarScope { async; generator })

(* return a fresh lexical scope *)
let fresh_lex () = fresh_impl LexScope
Expand Down Expand Up @@ -2520,7 +2520,8 @@ let string_of_scope = Scope.(
in

let string_of_scope_kind = function
| VarScope attrs -> spf "VarScope { async: %b }" attrs.async
| VarScope { async; generator } ->
spf "VarScope { async: %b; generator: %b }" async generator
| LexScope -> "Lex"
in

Expand Down
6 changes: 3 additions & 3 deletions src/typing/constraint_js.mli
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,8 @@ module Scope: sig
= Key.t

type var_scope_attrs = {
async: bool
async: bool;
generator: bool
}

type kind =
Expand All @@ -361,8 +362,7 @@ module Scope: sig
mutable refis: refi_binding KeyMap.t
}

val fresh: unit -> t
val fresh_async: unit -> t
val fresh: ?async:bool -> ?generator:bool -> unit -> t
val fresh_lex: unit -> t
val clone: t -> t

Expand Down
8 changes: 7 additions & 1 deletion src/typing/env_js.ml
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,13 @@ let clone_scopes scopes =
(* TODO will need to walk once LexScopes appear *)
let in_async_scope () = Scope.(
match (peek_scope ()).kind with
| VarScope { async } -> async
| VarScope { async; _ } -> async
| _ -> false
)

let in_generator_scope () = Scope.(
match (peek_scope ()).kind with
| VarScope { generator; _ } -> generator
| _ -> false
)

Expand Down
1 change: 1 addition & 0 deletions src/typing/env_js.mli
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ val get_scopes: unit -> Scope.t list
val clone_scopes: Scope.t list -> Scope.t list

val in_async_scope: unit -> bool
val in_generator_scope: unit -> bool

val all_entries: unit -> Entry.t SMap.t

Expand Down
Loading