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

Close over free variables in lambdas and object literals #1648

Merged
merged 11 commits into from
Mar 11, 2017
23 changes: 18 additions & 5 deletions packages/builtin_test/_test.pony
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ actor Main is TestList
test(_TestNextPow2)
test(_TestNumberConversionSaturation)
test(_TestMaybePointer)
test(_TestLambdaCapture)
test(_TestValtrace)


Expand Down Expand Up @@ -1224,7 +1225,7 @@ class iso _TestArrayFind is UnitTest
h.assert_eq[USize](1, a.find(1))
h.assert_eq[USize](5, a.find(1 where offset = 3))
h.assert_eq[USize](5, a.find(1 where nth = 1))
h.assert_error({()(a)? => a.find(6) })
h.assert_error({()? => a.find(6) })
h.assert_eq[USize](2, a.find(1 where
predicate = {(l: ISize, r: ISize): Bool => l > r }))
h.assert_eq[USize](0, a.find(0 where
Expand All @@ -1239,22 +1240,22 @@ class iso _TestArrayFind is UnitTest
h.assert_eq[USize](5, a.rfind(1))
h.assert_eq[USize](1, a.rfind(1 where offset = 3))
h.assert_eq[USize](1, a.rfind(1 where nth = 1))
h.assert_error({()(a)? => a.rfind(6) })
h.assert_error({()? => a.rfind(6) })
h.assert_eq[USize](4, a.rfind(1 where
predicate = {(l: ISize, r: ISize): Bool => l > r }))
h.assert_eq[USize](3, a.rfind(0 where
predicate = {(l: ISize, r: ISize): Bool => (l % 3) == r }))
h.assert_eq[USize](0, a.rfind(0 where
predicate = {(l: ISize, r: ISize): Bool => (l % 3) == r }, nth = 1))
h.assert_error({()(a)? =>
h.assert_error({()? =>
a.rfind(0 where
predicate = {(l: ISize, r: ISize): Bool => (l % 3) == r }, nth = 2)
})

var b = Array[_FindTestCls]
let c = _FindTestCls
b.push(c)
h.assert_error({()(b)? => b.find(_FindTestCls) })
h.assert_error({()? => b.find(_FindTestCls) })
h.assert_eq[USize](0, b.find(c))
h.assert_eq[USize](0, b.find(_FindTestCls where
predicate = {(l: _FindTestCls box, r: _FindTestCls box): Bool => l == r }
Expand Down Expand Up @@ -1534,7 +1535,7 @@ class iso _TestMaybePointer is UnitTest
let a = MaybePointer[_TestStruct].none()
h.assert_true(a.is_none())

h.assert_error({()(a)? => let from_a = a() })
h.assert_error({()? => let from_a = a() })

let s = _TestStruct
s.i = 7
Expand All @@ -1544,3 +1545,15 @@ class iso _TestMaybePointer is UnitTest

let from_b = b()
h.assert_eq[U32](s.i, from_b.i)


class iso _TestLambdaCapture is UnitTest
"""
Test free variable capture in lambdas.
"""
fun name(): String => "builtin/LambdaCapture"

fun apply(h: TestHelper) =>
let x = "hi"
let f = {(y: String): String => x + y}
h.assert_eq[String]("hi there", f(" there"))
6 changes: 3 additions & 3 deletions packages/collections/_test.pony
Original file line number Diff line number Diff line change
Expand Up @@ -210,15 +210,15 @@ class iso _TestRing is UnitTest

a.>push(4).>push(5)

h.assert_error({()(a)? => a(0) }, "Read ring 0")
h.assert_error({()(a)? => a(1) }, "Read ring 1")
h.assert_error({()? => a(0) }, "Read ring 0")
h.assert_error({()? => a(1) }, "Read ring 1")

h.assert_eq[U64](a(2), 2)
h.assert_eq[U64](a(3), 3)
h.assert_eq[U64](a(4), 4)
h.assert_eq[U64](a(5), 5)

h.assert_error({()(a)? => a(6) }, "Read ring 6")
h.assert_error({()? => a(6) }, "Read ring 6")

class iso _TestListsMap is UnitTest
fun name(): String => "collections/Lists/map()"
Expand Down
16 changes: 8 additions & 8 deletions packages/collections/persistent/_test.pony
Original file line number Diff line number Diff line change
Expand Up @@ -75,11 +75,11 @@ class iso _TestListApply is UnitTest
h.assert_eq[U32](l1(0), 1)
h.assert_eq[U32](l1(1), 2)
h.assert_eq[U32](l1(2), 3)
h.assert_error({()(l1)? => l1(3) })
h.assert_error({()(l1)? => l1(4) })
h.assert_error({()? => l1(3) })
h.assert_error({()? => l1(4) })

let l2 = Lists[U32].empty()
h.assert_error({()(l2)? => l2(0) })
h.assert_error({()? => l2(0) })

class iso _TestListValues is UnitTest
fun name(): String => "collections/persistent/List/values()"
Expand Down Expand Up @@ -253,7 +253,7 @@ class iso _TestMap is UnitTest

fun apply(h: TestHelper) =>
let m1: Map[String,U32] = Maps.empty[String,U32]()
h.assert_error({()(m1)? => m1("a") })
h.assert_error({()? => m1("a") })
let s1 = m1.size()
h.assert_eq[USize](s1, 0)

Expand Down Expand Up @@ -281,17 +281,17 @@ class iso _TestMap is UnitTest
h.assert_eq[U32](m7("b"), 3)
h.assert_eq[U32](m7("a"), 10)
let m8 = m7.remove("a")
h.assert_error({()(m8 = m8)? => m8("a") })
h.assert_error({()? => m8("a") })
h.assert_eq[U32](m8("b"), 3)
h.assert_eq[U32](m8("d"), 4)
h.assert_eq[U32](m8("e"), 5)
let m9 = m7.remove("e")
h.assert_error({()(m9 = m9)? => m9("e") })
h.assert_error({()? => m9("e") })
h.assert_eq[U32](m9("b"), 3)
h.assert_eq[U32](m9("d"), 4)
let m10 = m9.remove("b").remove("d")
h.assert_error({()(m10 = m10)? => m10("b") })
h.assert_error({()(m10 = m10)? => m10("d") })
h.assert_error({()? => m10("b") })
h.assert_error({()? => m10("d") })
else
h.complete(false)
end
Expand Down
24 changes: 12 additions & 12 deletions packages/collections/persistent/benchmarks/main.pony
Original file line number Diff line number Diff line change
Expand Up @@ -4,51 +4,51 @@ use "ponybench"
actor Main
new create(env: Env) =>
let bench = PonyBench(env)

var map = Maps.empty[U64, U64]()
try map = map.update(0, 0) end

bench[Map[U64, U64]](
"insert level 0",
{()(map): Map[U64, U64] ? => map.update(1, 1)} val)
{(): Map[U64, U64] ? => map.update(1, 1)} val)

bench[U64](
"get level 0",
{()(map): U64 ? => map(0)} val)
{(): U64 ? => map(0)} val)

bench[Map[U64, U64]](
"update level 0",
{()(map): Map[U64, U64] ? => map.update(0, 1)} val)
{(): Map[U64, U64] ? => map.update(0, 1)} val)

bench[Map[U64, U64]](
"delete level 0",
{()(map): Map[U64, U64] ? => map.remove(0)} val)
{(): Map[U64, U64] ? => map.remove(0)} val)

bench[Map[U64, U64]](
"create sub-node",
{()(map): Map[U64, U64] ? => map.update(32, 32)} val)
{(): Map[U64, U64] ? => map.update(32, 32)} val)

// expand index 0 into 2 sub-nodes
try map = map.update(32, 32) end

bench[Map[U64, U64]](
"remove sub-node",
{()(map): Map[U64, U64] ? => map.remove(32)} val)
{(): Map[U64, U64] ? => map.remove(32)} val)

bench[Map[U64, U64]](
"insert level 1",
{()(map): Map[U64, U64] ? => map.update(1, 1)} val)
{(): Map[U64, U64] ? => map.update(1, 1)} val)

bench[U64](
"get level 1",
{()(map): U64 ? => map(0)} val)
{(): U64 ? => map(0)} val)

bench[Map[U64, U64]](
"update level 1",
{()(map): Map[U64, U64] ? => map.update(0, 1)} val)
{(): Map[U64, U64] ? => map.update(0, 1)} val)

try map = map.update(1, 1) end

bench[Map[U64, U64]](
"delete level 1",
{()(map): Map[U64, U64] ? => map.remove(1)} val)
{(): Map[U64, U64] ? => map.remove(1)} val)
16 changes: 8 additions & 8 deletions packages/itertools/_test.pony
Original file line number Diff line number Diff line change
Expand Up @@ -401,7 +401,7 @@ class iso _TestIterLast is UnitTest

fun apply(h: TestHelper) ? =>
let input1 = Array[I64]
h.assert_error({()(input1) ? => Iter[I64](input1.values()).last() })
h.assert_error({()? => Iter[I64](input1.values()).last() })
let input2 =[as I64: 1]
h.assert_eq[I64](
1,
Expand All @@ -423,7 +423,7 @@ class iso _TestIterMap is UnitTest
let expected = ["ab", "bb", "cb"]
let actual = Array[String]

let fn = { (x: String): String => x + "b" }
let fn = {(x: String): String => x + "b" }
for x in Iter[String](input.values()).map[String](fn) do
actual.push(x)
end
Expand All @@ -444,7 +444,7 @@ class iso _TestIterNth is UnitTest
h.assert_eq[USize](
3,
Iter[USize](input.values()).nth(3))
h.assert_error({()(input) ? => Iter[USize](input.values()).nth(4) })
h.assert_error({()? => Iter[USize](input.values()).nth(4) })

class iso _TestIterRun is UnitTest
fun name(): String => "itertools/Iter.run"
Expand All @@ -454,26 +454,26 @@ class iso _TestIterRun is UnitTest
h.expect_action("2")
h.expect_action("3")
h.expect_action("error")

let xs = [as I64: 1, 2, 3]

h.long_test(100_000_000)

Iter[I64](xs.values())
.map[None]({(x: I64)(h) => h.complete_action(x.string()) })
.map[None]({(x: I64) => h.complete_action(x.string()) })
.run()

Iter[I64](object ref is Iterator[I64]
fun ref has_next(): Bool => true
fun ref next(): I64 ? => error
end).run({()(h) => h.complete_action("error") })
end).run({() => h.complete_action("error") })

class iso _TestIterSkip is UnitTest
fun name(): String => "itertools/Iter.skip"

fun apply(h: TestHelper) ? =>
let input = [as I64: 1]
h.assert_error({()(input) ? =>
h.assert_error({()? =>
Iter[I64](input.values()).skip(1).next()
})
input.push(2)
Expand Down
20 changes: 10 additions & 10 deletions packages/itertools/iter.pony
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ class Iter[A] is Iterator[A]

fun ref any(f: {(A!): Bool ?} box): Bool =>
"""
Return true if at least one value of the iterator matches the predicate
Return true if at least one value of the iterator matches the predicate
`f`. This method short-circuits at the first value where the predicate
returns true, otherwise false is returned.

Expand Down Expand Up @@ -87,7 +87,7 @@ class Iter[A] is Iterator[A]
fun ref collect[B: Seq[A!] ref = Array[A!]](coll: B): B^ =>
"""
Push each value from the iterator into the collection `coll`.

## Example

```pony
Expand Down Expand Up @@ -190,7 +190,7 @@ class Iter[A] is Iterator[A]
fun ref filter(f: {(A!): Bool ?} box): Iter[A]^ =>
"""
Return an iterator that only returns items that match the predicate `f`.

## Example

```pony
Expand Down Expand Up @@ -249,7 +249,7 @@ class Iter[A] is Iterator[A]
fun ref find(f: {(A!): Bool ?} box, n: USize = 1): A ? =>
"""
Return the nth value in the iterator that satisfies the predicate `f`.

## Examples

```pony
Expand Down Expand Up @@ -279,7 +279,7 @@ class Iter[A] is Iterator[A]
fun ref fold[B](f: {(B, A!): B^ ?} box, acc: B): B^ ? =>
"""
Apply a function to every element, producing an accumulated value.

## Example

```pony
Expand Down Expand Up @@ -315,7 +315,7 @@ class Iter[A] is Iterator[A]
"""
Return an iterator where each item's value is the application of the given
function to the value in the original iterator.

## Example

```pony
Expand Down Expand Up @@ -372,7 +372,7 @@ class Iter[A] is Iterator[A]

```pony
Iter[I64]([as I64: 1, 2, 3].values())
.map[None]({(x: I64)(env) => env.out.print(x.string()) })
.map[None]({(x: I64) => env.out.print(x.string()) })
.run()
```
```
Expand All @@ -382,8 +382,8 @@ class Iter[A] is Iterator[A]
```
"""
if not _iter.has_next() then return end
try
_iter.next()
try
_iter.next()
run(on_error)
else
on_error()
Expand Down Expand Up @@ -413,7 +413,7 @@ class Iter[A] is Iterator[A]
fun ref skip_while(f: {(A!): Bool ?} box): Iter[A]^ =>
"""
Skip values of the iterator while the predicate `f` returns true.

## Example

```pony
Expand Down
6 changes: 3 additions & 3 deletions packages/itertools/itertools.pony
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Elixir's Enum and Stream.
## Iter

The Iter class wraps iterators so that additional methods may be applied to it.
Some methods, such as fold and collect, run through the underlying iterator in
Some methods, such as fold and collect, run through the underlying iterator in
order to return a result. Others, such as map and filter, are lazy. This means
that they return another Iter so that the resulting values are computed one by
one as needed. Lazy methods return Iter types.
Expand All @@ -21,7 +21,7 @@ any odd numbers, and prints the rest.
let xs = Iter[I64]([as I64: 1, 2, 3, 4, 5].values())
.map[I64]({(x: I64): I64 => x + 1 })
.filter({(x: I64): Bool => (x % 2) == 0 })
.map[None]({(x: I64)(env) => env.out.print(x.string()) })
.map[None]({(x: I64) => env.out.print(x.string()) })
```

This will result in an iterator that prints the numbers 2, 4, and 6. However,
Expand All @@ -43,7 +43,7 @@ for a loop. So the final code would be as follows:
Iter[I64]([as I64: 1, 2, 3, 4, 5].values())
.map[I64]({(x: I64): I64 => x + 1 })
.filter({(x: I64): Bool => (x % 2) == 0 })
.map[None]({(x: I64)(env) => env.out.print(x.string()) })
.map[None]({(x: I64) => env.out.print(x.string()) })
.run()
```

Expand Down
2 changes: 1 addition & 1 deletion packages/ponybench/_bench_async.pony
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ actor _BenchAsync[A: Any #share] is _Benchmark
else
try
let n: _BenchAsync[A] tag = this
_f().next[None]({(a: A)(n, i) => n._run(i+1)} iso)
_f().next[None]({(a: A) => n._run(i + 1)} iso)
else
_notify._failure(_name, false)
end
Expand Down
8 changes: 4 additions & 4 deletions packages/promises/_test.pony
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,16 @@ class iso _TestPromise is UnitTest
fun _test_fulfilled(h: TestHelper) =>
h.expect_action("fulfilled")
let p = Promise[String]
p.next[None]({(s: String)(h) => h.complete_action(s) } iso)
p.next[None]({(s: String) => h.complete_action(s) } iso)
p("fulfilled")

fun _test_rejected(h: TestHelper) =>
h.expect_action("rejected")
let p = Promise[String]
p.next[String](
{(s: String)(h): String ? => error } iso,
{()(h): String => "rejected" } iso
{(s: String): String ? => error } iso,
{(): String => "rejected" } iso
).next[None](
{(s: String)(h) => h.complete_action(s) } iso
{(s: String) => h.complete_action(s) } iso
)
p.reject()
Loading