Skip to content

Commit

Permalink
fix(iterables): iterables are non-stateful
Browse files Browse the repository at this point in the history
BREAKING CHANGE: iterables were stateful and one could only iterate over them once, refferential transparency could not be guaranteed and extra care had to be taken, caching their items using `[...iterable]`. Now iterables are not stateful anymore and can be reused and iterated over multiple times.
  • Loading branch information
kollhof committed Oct 24, 2020
1 parent cc4801b commit 6738618
Show file tree
Hide file tree
Showing 13 changed files with 636 additions and 591 deletions.
10 changes: 8 additions & 2 deletions src/js/async.fnk
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
babel_types = import '@babel/types'
{isArrowFunctionExpression, isFunctionExpression, isForOfStatement} = babel_types
{
isArrowFunctionExpression, isForOfStatement, isObjectMethod
} = babel_types
{set_props} = import '@fink/js-interop/reflect.fnk'


Expand All @@ -12,8 +14,12 @@ transform_async = fn path:
isArrowFunctionExpression ?:
set_props path.node, {async: true}

isFunctionExpression ?:
# generators for iterables
isObjectMethod ?:
set_props path.node, {async: true}
match path.node:
{key: {property: {name: 'iterator'}}}:
set_props path.node.key.property, {name: 'asyncIterator'}

isForOfStatement ?:
set_props path.node, {await: true}
Expand Down
26 changes: 23 additions & 3 deletions src/js/types.fnk
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ babe_types = import '@babel/types'
expressionStatement, functionExpression
blockStatement, identifier, arrowFunctionExpression
logicalExpression, binaryExpression, unaryExpression
doExpression, assignmentExpression, isIdentifier
doExpression, assignmentExpression, isIdentifier, objectExpression,
returnStatement, objectMethod, memberExpression
} = babe_types


Expand Down Expand Up @@ -101,10 +102,29 @@ func = fn args, ...expressions:
expr_block ...expressions


symbol = fn symb:
memberExpression
identifier 'Symbol'
identifier symb


computed = true
async = true


generator = fn name, args, ...statements:
func_name = ident name
body = blockStatement statements
functionExpression func_name, args, body, true

functionExpression func_name, args,
blockStatement list:
returnStatement
objectExpression list:
objectMethod
'method', (symbol 'iterator'), []
blockStatement list:
...statements
computed
async


for_of = fn [item, items], ...expressions:
Expand Down
64 changes: 37 additions & 27 deletions src/lang/async/init.test.fnk.snap
Original file line number Diff line number Diff line change
Expand Up @@ -8,33 +8,38 @@ export const task3 = async (foo) => {
const spam = await foo();
return bar + 123;
};
export const a_gen = async function* unfold(ˆinitial_2) {
let ˆaccu_3 = 0;
export const a_gen = function unfold(ˆinitial_2) {
return {
async *[Symbol.asyncIterator]() {
let ˆaccu_3 = 0;
while (true) {
const curr = ˆaccu_3;
while (true) {
const curr = ˆaccu_3;
let _do_result;
let _do_result;
ˆmatch_5: {
const ˆvalue_4 = shrub;
ˆmatch_5: {
const ˆvalue_4 = shrub;
if (ˆvalue_4 === spam) {
_do_result = await ni(curr);
break ˆmatch_5;
}
ifvalue_4 === spam) {
_do_result = await ni(curr);
break ˆmatch_5;
}
{
_do_result = curr + 1;
break ˆmatch_5;
{
_do_result = curr + 1;
break ˆmatch_5;
}
}
const ˆresult_1 = _do_result;
_do_result = undefined;
yield ˆresult_1;
ˆaccu_3 = ˆresult_1;
}
}
const ˆresult_1 = _do_result;
_do_result = undefined;
yield ˆresult_1;
ˆaccu_3 = ˆresult_1;
}
};
};
await ni;"
`;
Expand All @@ -48,15 +53,20 @@ exports[`await handles awaiting async iterables 1`] = `
{
let ˆpipe_result_3 = undefined;
_do_result2 = ˆpipe_result_3 = async function* unfold(ˆinitial_5) {
let ˆaccu_6 = 0;
_do_result2 = ˆpipe_result_3 = function unfold(ˆinitial_5) {
return {
async *[Symbol.asyncIterator]() {
let ˆaccu_6 = 0;
while (true) {
const cntr = ˆaccu_6;
const ˆresult_4 = (await cntr) + 1;
yield ˆresult_4;
ˆaccu_6 = ˆresult_4;
}
while (true) {
const cntr = ˆaccu_6;
const ˆresult_4 = (await cntr) + 1;
yield ˆresult_4;
ˆaccu_6 = ˆresult_4;
}
}
};
}(ˆpipe_result_3);
}
const ˆitems_1 = _do_result2;
Expand Down
2 changes: 1 addition & 1 deletion src/lang/call/pipe.test.fnk
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@ describe 'pipe', fn:
spam
[...?]


pipe [1, 2, 3]:
map item: item * 2
'
to_match_snapshot



describe 'small pipe |', fn:
it 'pipes', fn:
expect
Expand Down
17 changes: 11 additions & 6 deletions src/lang/call/pipe.test.fnk.snap
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,17 @@ exports[`pipe compiles 1`] = `
{
let ˆpipe_result_2 = [1, 2, 3];
ˆpipe_result_2 = function* map(ˆitems_4) {
for (const ˆitem_3 of ˆitems_4) {
const item = ˆitem_3;
const ˆresult_5 = item * 2;
yield ˆresult_5;
}
ˆpipe_result_2 = function map(ˆitems_4) {
return {
*[Symbol.iterator]() {
for (const ˆitem_3 of ˆitems_4) {
const item = ˆitem_3;
const ˆresult_5 = item * 2;
yield ˆresult_5;
}
}
};
}(ˆpipe_result_2);
}"
`;
Expand Down
151 changes: 93 additions & 58 deletions src/lang/iterable/filter.test.fnk.snap
Original file line number Diff line number Diff line change
@@ -1,85 +1,120 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`filter async compiles 1`] = `
"(async function* filter(ˆitems_2) {
for await (const ˆitem_1 of ˆitems_2) {
const item = ˆitem_1;
const ˆresult_3 = (await item) % 2 === 0;
if (ˆresult_3) yield ˆitem_1;
}
"(function filter(ˆitems_2) {
return {
async *[Symbol.asyncIterator]() {
for await (const ˆitem_1 of ˆitems_2) {
const item = ˆitem_1;
const ˆresult_3 = (await item) % 2 === 0;
ifresult_3) yield ˆitem_1;
}
}
};
});"
`;

exports[`filter compiles 1`] = `
"(function* filter(ˆitems_2) {
for (const ˆitem_1 of ˆitems_2) {
const item = ˆitem_1;
const ˆresult_3 = item % 2 === 0;
if (ˆresult_3) yield ˆitem_1;
}
"(function filter(ˆitems_2) {
return {
*[Symbol.iterator]() {
for (const ˆitem_1 of ˆitems_2) {
const item = ˆitem_1;
const ˆresult_3 = item % 2 === 0;
ifresult_3) yield ˆitem_1;
}
}
};
});
(function* filter(ˆitems_5) {
for (const ˆitem_4 of ˆitems_5) {
const [a, ...b] = ˆitem_4;
const a_alone = a(andis_empty(b));
const ˆresult_6 = a_alone;
ifresult_6) yield ˆitem_4;
}
(function filter(ˆitems_5) {
return {
*[Symbol.iterator]() {
for (const ˆitem_4 of ˆitems_5) {
const [a, ...b] = ˆitem_4;
const a_alone = a(andis_empty(b));
const ˆresult_6 = a_alone;
ifresult_6) yield ˆitem_4;
}
}
};
});
(function* filter(ˆitems_9) {
for (const ˆitem_7 of ˆitems_9) {
let _do_result;
(function filter(ˆitems_9) {
return {
*[Symbol.iterator]() {
for (const ˆitem_7 of ˆitems_9) {
let _do_result;
{
const [...ˆitems_8] = ˆitem_7;
_do_result =items_8.slice(0, -1), ˆitems_8.slice(-1)];
{
const [...ˆitems_8] = ˆitem_7;
_do_result =items_8.slice(0, -1), ˆitems_8.slice(-1)];
}
const [a, [b]] = _do_result;
_do_result = undefined;
const ˆresult_10 = b && is_empty(a);
ifresult_10) yield ˆitem_7;
}
}
const [a, [b]] = _do_result;
_do_result = undefined;
const ˆresult_10 = b && is_empty(a);
ifresult_10) yield ˆitem_7;
}
};
});"
`;

exports[`filter compiles with accu 1`] = `
"(function* filter(ˆitems_3) {
let ˆaccu_2 = 0;
for (const ˆitem_1 of ˆitems_3) {
const item = ˆitem_1;
const cntr = ˆaccu_2;
const ˆresult_4 = item < 1 && cntr % 2 === 0;
if (ˆresult_4) yield ˆitem_1;
ˆaccu_2 = cntr + 1;
}
"(function filter(ˆitems_3) {
return {
*[Symbol.iterator]() {
let ˆaccu_2 = 0;
for (const ˆitem_1 of ˆitems_3) {
const item = ˆitem_1;
const cntr = ˆaccu_2;
const ˆresult_4 = item < 1 && cntr % 2 === 0;
ifresult_4) yield ˆitem_1;
ˆaccu_2 = cntr + 1;
}
}
};
});"
`;

exports[`filter compiles with accu 2`] = `
"(function* filter(ˆitems_3) {
let ˆaccu_2 = false;
for (const ˆitem_1 of ˆitems_3) {
const is_even = ˆaccu_2;
const ˆresult_4 = is_even;
if (ˆresult_4) yield ˆitem_1;
ˆaccu_2 = !is_even;
}
"(function filter(ˆitems_3) {
return {
*[Symbol.iterator]() {
let ˆaccu_2 = false;
for (const ˆitem_1 of ˆitems_3) {
const is_even = ˆaccu_2;
const ˆresult_4 = is_even;
ifresult_4) yield ˆitem_1;
ˆaccu_2 = !is_even;
}
}
};
});"
`;

exports[`filter compiles with accu 3`] = `
"(function* filter(ˆitems_3) {
let ˆaccu_2 = false;
for (const ˆitem_1 of ˆitems_3) {
const is_even = ˆaccu_2;
const ˆresult_4 = is_even;
if (ˆresult_4) yield ˆitem_1;
ˆaccu_2 = !is_even;
}
"(function filter(ˆitems_3) {
return {
*[Symbol.iterator]() {
let ˆaccu_2 = false;
for (const ˆitem_1 of ˆitems_3) {
const is_even = ˆaccu_2;
const ˆresult_4 = is_even;
ifresult_4) yield ˆitem_1;
ˆaccu_2 = !is_even;
}
}
};
});"
`;
Loading

0 comments on commit 6738618

Please sign in to comment.