# Closure

December 20, 2021

## Toy 語法

``````def selection(number) {
# 找出未排序中最小值
def min(m, j) {
if j == number.length() {
return m
}
if number.get(j) < number.get(m) {
return min(j, j + 1)
}

return min(m, j + 1)
}

i = 0
while i < number.length() {
m = min(i, i + 1)
number.swap(i, m)
i += 1
}
}

number = [1, 5, 2, 3, 9, 7]
selection(number)
println(number)     # 顯示 [1, 2, 3, 5, 7, 9]
``````

``````def gcd(m, n) {
if n == 0 {
return m
}
return gcd(n, m % n)
}

println(gcd(20, 30))         # 顯示 10
println(gcd.class())         # 顯示 <Class Function>

gcd2 = gcd
println(gcd2(20, 30))        # 顯示 10
``````

``````def printFoo() {
println('Foo')
}

iterate(0, 5).forEach(printFoo) # 顯示 5 行 Foo
``````

``````def doSome() {
x = 10
def f(y) {
return x + y
}
return f
}

foo = doSome()
println(foo(20))  # 顯示 30
println(foo(30))  # 顯示 40
``````

``````def f(y) {
return x + y
}
``````

Toy Lang 中每個函式，都會記錄外部函式的環境物件，當 `f``doSome` 傳回，`f` 會記得 `doSome` 的環境物件，而在查找 `x` 時，由於 `f` 本身沒有該名稱，這時會看看本身記錄的環境物件，也就是 `doSome` 的環境物件，這時仍然可以找到，因此才可以順利執行傳回的函式。

``````def doSome() {
x = 10
def f(y) {
nonlocal x = x + y
return x
}
return f
}

foo = doSome()
println(foo(20))  # 顯示 30
println(foo(30))  # 顯示 60
``````

``````def doSome() {
x = 10
def f(y) {
nonlocal x = x + y
return x
}
return f
}

foo1 = doSome()
foo2 = doSome()
println(foo1(20))  # 顯示 30
println(foo2(20))  # 顯示 30
``````

## Toy 實作

``````class Func extends Value {
constructor(params, stmt, name = '', parentContext = null) {
super();
this.params = params;
this.stmt = stmt;
this.name = name;
this.parentContext = parentContext;
}

...

call(context, args) {
const ctxValues = evaluateArgs(context, args);
if(ctxValues.length !== 0) {
const ctxValue = ctxValues.slice(-1)[0];
if(ctxValue.thrownNode) {
return ctxValue;
}
}

const bodyStmt = this.bodyStmt(context, ctxValues);
return bodyStmt.evaluate(
this.parentContext ?
this.parentContext.childContext() : // closure context
context.childContext()
);
}

withParentContext(context) {
return new Func(this.params, this.stmt, this.name, context);
}

clzOfLang(context) {
return context.lookUpVariable('Function');;
}

evaluate(context) {
return new Instance(
this.clzOfLang(context), new Map(), this.withParentContext(context)
);
}
}
``````