pub enum Closure(P, D = Any, S = Any) -> T { Read(ReadClosure), // reads self (where Self is Closure) and/or environment ReadRef(ReadRefClosure), // reads environment/self, return value tied to closure Mut(MutClosure), // mutates self/environment MutRef(MutRefClosure), // mutates self/environment, return value tied to closure Cons(ConsClosure), // consumes self/environment } class Foo(bar: Bar) {} class Bar(mut i: Int) {} fn readClosure(cl: Closure(Int) -> String) -> String { cl(42) } fn useReadClosure() { let s = readClosure { i -> 'i: ' + i.toString() } assertEq('i: 42', s) } fn mutClosure(cl: mut Closure(Int) -> String) -> String { cl(42) } fn useMutClosure() { let mut x = 0 let cl: mut Closure(Int) -> String = mut { i -> let result = 'Val: ' + (i + x) x++ result } let s0 = mutClosure cl assertEq('Val: 42', s0) let s1 = mutClosure cl assertEq('Val: 43', s1) } fn consClosure(cl: cons Closure(Int) -> String) -> String { cl(42) // cl(43) // Error! cl was already consumed } fn useConsClosure() { let foo = Foo(Bar(10)) let cl: cons Closure(Int) -> String = cons { i -> let fooBarI = foo.bar.i drop(foo) 'Val: ' + (i + fooBarI) } let s = consClosure(cl) // let err0 = consClosure(cl) // Error! cl may have been consumed // let err1 = foo // Error! foo may have been consumed assertEq('Val: 52', s) } fn readRefClosure(cl: Closure(foo: Foo) -> Bar ref foo) -> Int { let foo = Foo(Bar(42)) let bar = cl(foo) bar.i } fn useReadRefClosure() { let i = readRefClosure { foo -> foo.bar } assertEq(42, i) } fn mutRefClosure(cl: mut Closure(foo: Foo) -> Bar ref foo) -> Int { let foo = Foo(Bar(42)) let bar = cl(foo) bar.i } fn useMutRefClosure() { let i = mutRefClosure mut { foo -> foo.bar.i = 84 foo.bar } assertEq(84, i) } fn readClosureWithDelegate(cl: Closure(P = Int, D = Foo) -> String) -> String { cl(42) } fn useReadClosureWithDelegate() { let foo = Foo(Bar(42)) let cl = { i: Int -> 'Val: ' + (i + bar.i) // bar found in delegate } cl.delegate = &foo let s = readClosureWithDelegate(cl) assertEq('Val: 84', s) } class Baz(foo: Foo) {} fn mutRefClosureWithDelegate(baz: &mut Baz, cl: Closure(Int, baz: &mut Baz)(D = Foo) -> &Bar ref baz) -> String { let foo = Foo(Bar(1)) cl.delegate = &foo let bar = cl(2, baz) 'Val: ' + bar.i } fn useMutRefClosureWithDelegate() { let cl: Closure(Int, baz: &mut Baz)(D = Foo) -> &Bar ref baz = mut { i, baz -> let iFromDelegate = bar.i baz.foo.bar.i = iFromDelegate + i &baz.foo.bar } let baz = Baz(Foo(Bar(20))) let bar = mutRefClosureWithDelegate(cl) assertEq(3, bar.i) } fn mutClosureWithDelegate(cl: Closure(P = (Int, Int), D = mut Foo) -> String { let foo = Foo(Bar(42)) cl.delegate = &mut foo cl(1, 2) } fn useMutClosureWithDelegate() { let cl: Closure(P = (Int, Int), D = mut Foo) -> String = mut { x, y -> bar.i = 3 'Val: ' + (x + y + bar.i) } let s = mutClosureWithDelegate(cl) assertEq('Val: 6', s) } pub int ClosureBase { params: Array mut resolutionStrategy: ResolutionStrategy ref fn getDelegate() -> Option<&Any> ref fn getSelfObject() -> Option<&Any> } pub enum ResolutionStrategy { DelegateFirst, DelegateOnly, SelfFirst, SelfOnly, None } pub int ConsClosure : ClosureBase { mut fn setDelegate(d: Any) -> Void mut fn setSelf(s: Any) -> Void mut fn call(...args: Array) -> T def mut op () (...args: Array T = call(...args) } pub int MutClosure : ClosureBase { mut fn setDelegate(d: &mut Any) -> Void mut fn setSelf(s: &mut Any) -> Void mut fn call(...args: Array) -> T def mut op () (...args: Array) -> T = call(...args) } pub int MutRefClosure : ClosureBase { mut fn setDelegate(d: &mut Any) -> Void mut fn setSelf(s: &mut Any) -> Void mut ref fn call(...args: Array) -> T // T ref self def mut ref op () (...args: Array) -> T = call(...args) } pub int ReadClosure : ClosureBase { mut fn setDelegate(d: &Any) -> Void mut fn setSelf(s: &Any) -> Void fn call(...args: Array) -> T def op () (...args: Array) -> T = call(...args) } pub int ReadRefClosure : ClosureBase { mut fn setDelegate(d: &Any) -> Void mut fn setSelf(s: &Any) -> Void ref fn call(...args: Array) -> T def op () (...args) -> T = call(..args) } pub int ClosureParam { clazz: Class def op typeof () -> Class = clazz } // Example of how closures are compiled fn main() { let is = [1, 2, 3] let cl0 = { acc: String, i: Int -> acc + i } println is.reduce(cl0) let x = 10 let cl1 = /* read */ { acc: String, i: Int -> acc + (i + x) } println is.reduce(cl1) let mut counter = 0 let cl2 = mut { acc: String, i: Int -> let result = acc + (i + counter) counter++ result } println is.reduce(cl2) let mut foo = Foo(Bar(1)) let cl3 = cons { acc: String, i: Int -> // do something pointless just for illustration if foo.bar.y % 2 == 0 { foo.bar = Bar(1) } let result = acc + (i + foo.bar.y) foo.bar.y++ result } // foo.bar = Bar(42) // Error! foo moved into consuming cl3 println [1, 2, 3, 4].reduce(cl3) // "11243645" } // Compiled @Synthetic fn closure_main_cl0(acc: String, i: Int) = acc + i @Synthetic class Closure_main_cl1_env(x: &Int) -> String {} @Synthetic class Closure_main_cl1(env: Closure_main_cl1_env) : ReadClosure { impl fn call(...args: Array) -> String { let acc: String = args[0] let i: Int = args[1] let x: Int = *env.x // because env.getX() -> &&Int acc + (i + x) } } @Synthetic class Closure_main_cl2_env(counter: &mut Int) {} @Synthetic class Closure_main_cl2(env: Closure_main_cl2_env) : MutClosure { impl mut fn call(...args: Array) -> String { let acc: String = args[0] let i: Int = args[1] let counter = &mut env.counter let result = acc + (i + counter) *counter++ result } } @Synthetic class Closure_main_cl3(mut foo: Foo) : ConsClosure { impl mut fn call(...args: Array) -> String { let acc: String = args[0] let i: Int = args[1] if foo.bar.y % 3 == 0 { foo.bar = Bar(1) } let result = acc + (i + foo.bar.y) foo.bar.y++ result } } fn main() { let is: List = ArrayList() { add 1 add 2 add 3 } let cl0 = &closure_main_cl0 println is.reduce(cl0) let x = 10 let cl1_env = HashMap() { put 'x', &x } let cl1: Closure = Closure::ReadClosure(Closure_main_cl1(cl1_env)) println is.reduce(cl1) let mut counter = 0 let cl2_env = Closure_main_cl2_env(&counter) let cl2: Closure = Closure::MutClosure(Closure_main_cl2(cl2_env)) println is.reduce(cl2) let mut foo = Foo(Bar(1)) let cl3: Closure = Closure::ConsClosure(Closure_main_cl3(foo)) // foo moved into closure let tmp0: List = ArrayList() { addAll 1, 2, 3, 4 } println tmp0.reduce(cl3) } // Example // original class Foo { mut fld current = 0 ref fn getClosure(base: Int) -> (mut Closure(Int) -> Int) { return mut { given -> let result = base + given + current current++ result } } } fn main() { let foo = Foo() let cl = foo.getClosure(10) let nums = [cl(1), cl(2), cl(3), cl(4)] assertEq([11, 13, 15, 17], nums) } // Compiles to @Synthetic class Closure_Foo_getClosure_0(base: &Int, current: &mut Int) : MutClosure(Int) -> Int { impl mut fn call(given: Int) -> Int { let result = *base + given + *current *current++ result } } class Foo { mut fld current = 0 ref fn getClosure(base: Int) -> (mut Closure(Int) -> Int) { return Closure_Foo_getClosure_0(&base, &mut current) } } fn main() { /* ... */ } // Library code pub int ReadClosure(P : () = ()) : Into[Self(P), Option] { mut fn setDelegate(d: D) -> Void ref fn getDelegate() -> Option<&D> mut fn setSelfObject(s: &S) -> Void ref fn getSelfObject() -> Option<&S> fn call(...params: P) -> T def op () (...params: P) -> T = call(...params) def cons fn intoDelegate() -> D { Into[Self(P), Option].into() } } pub abstract class AbstractReadClosure(P : () = ()) : ReadClosure(P) { mut fld delegate: Option mut fld selfObject: Option<&S> impl mut fn setDelegate(d: D) { delegate = Some(d) } impl ref fn getDelegate() -> Option<&D> { &delegate is Some(d) ? Some(d) : None } impl mut fn setSelfObject(s: &S) { selfObject = Some(s) } impl ref fn getSelfObject() -> Option<&S> { &selfObject is Some(s) ? Some(s) : None } impl Into[Self(P), D] { cons fn into() -> Option = delegate } } // User code demonstration class Foo(bar: Bar) {} class Bar(num: Int) {} class Baz(foo: Foo) { pub ref fn getClosure() -> Closure { return { foo.bar.num + bar.num } } } fn main() { let baz0 = Baz(Foo(Bar(1))) let cl = baz0.getClosure() let delegate = Foo(Bar(2)) cl.delegate = delegate let result0 = cl() assertEq(3, result0) let baz1 = Baz(Foo(Bar(2))) cl.selfObject = &baz1 let result1 = cl() assertEq(4, result1) let movedDelegate = cl.intoDelegate() assertEq(2, movedDelegate.bar.num) } // User code compiles to class Foo(bar: Bar) {} class Bar(num: Int) {} @Synthetic class Closure_Baz_getClosure_0: AbstractReadClosure() { ctor(selfObject: &Baz) ref selfObject { self.selfObject = Some(selfObject) } fn call() -> Int { let t0 = selfObject.unwrap().foo.bar.num let t1 = self.delegate.unwrap().bar.num t0 + t1 } } class Baz(foo: Foo) { pub ref fn getClosure() -> Closure { return Closure_Baz_getClosure_0(&self) } } fn main() { // ... } // fn vs Closure syntax int FnOrClosureTaker { fn read(f: fn (Int) -> Int) -> Int fn mut(f: mut fn (Int) -> Int) -> Int fn cons(f: cons fn (Int) -> Int) -> Int } class FnOrClosureTakerImpl : FnOrClosureTaker { impl fn read(f: fn (Int) -> Int) { let i0 = f(0) let i1 = f(1) let i2 = f(2) i0 + i1 + i2 } impl fn mut(f: mut fn (Int) -> Int) { /* same as read */ } impl fn cons(f: cons fn (Int) -> Int) { let i0 = f() // let i1 = f() // Error! f already consumed i0 } } mod User { fn getReader() -> fn (Int) -> Int { return { it * 2 } } fn useReader() { let reader = getReader() let fnOrClosureTaker: FnOrClosureTaker = FnOrClosureTakerImpl() let result = fnOrClosureTaker.read(reader) assertEq(6, result) } } // compiles to mod User { fn getReader_closure_0(it: Int) -> Int { it * 2 } fn getReader() -> &fn (Int) -> Int { &getReader_closure_0 } // etc. } // New interfaces? pub int ReadClosure(P : () = ()) -> T : fn (P) -> T { ref fn getDelegate() -> Option<&D> mut fn setDelegate(d: D) -> Void fn call(...p: P) -> T def op () alias call } pub int Iterator { mut fn next() -> Option } pub int Iterable : Into[Self, Iterator] { ref fn iterator() -> Iterator<&T> def cons fn intoIterator() = Into[Self, Iterator].into(self) } pub class MyIterable : Iterable { fld items: List = [1, 2, 3] impl ref fn iterator() -> Iterator<&Int> { let mut index = 0 return mut { if index >= items.length { None } else { let next = items[index] index++ Some(next) } } } }