Instructor: James Riely
int loop (int n, int result) {
if (n <= 1) {
return result;
} else {
return loop (n - 1, n * result);
}
}
int fact (int n) {
return loop (n, 1);
}
int fact (int n) {
int loop (int n, int result) {
if (n <= 1) {
return result;
} else {
return loop (n - 1, n * result);
}
}
return loop (n, 1);
}
$ gcc -c nested-fact.c
$ gcc -pedantic -c nested-fact.c
function.c: In function ‘fact’:
function.c:2:3: warning: ISO C forbids nested functions [-pedantic]
int fact (int n) {
int loop (int i, int result) {
if (i > n) {
return result;
} else {
return loop (i+1, i * result);
}
}
return loop (1, 1);
}
def printElt [A,B] (f:A=>B) (x:A) : B =
println (x)
f (x)
def mapDebug [A,B] (xs:List[A], f:A=>B) : List[B] =
xs.map (printElt (f))
def mapDebug [A,B] (xs:List[A], f:A=>B) : List[B] =
def printElt (x:A) : B =
println (x)
f (x) // use f from enclosing context
xs.map (printElt)
def mapDebug [A,B] (xs:List[A], f:A=>B) : List[B] =
xs.map ((x:A) => { println (x); f (x) }) // anonymous function clearer
#include <stdio.h>
#include <stdlib.h>
typedef void (*funcptr) (int);
funcptr f (int x) {
void g (int y) {
printf ("x = %d, y = %d\n", x, y);
}
g (1);
return &g;
}
int main (void) {
funcptr h = f (10);
(*h) (2);
f (20);
(*h) (3);
}
$ gcc -std=c99 nested-gcc.c
$ ./a.out
x = 10, y = 1 <- safe to call g, with x=10
x = 10, y = 2 <- unsafe to call h, created with x=10, GOOD!
x = 20, y = 1 <- safe to call g
x = 20, y = 3 <- unsafe to call h, created with x=10, BAD!
#include <stdio.h>
#include <stdlib.h>
#include <Block.h>
// ^funcptr for blocks; *funcptr for function pointers
typedef void (^funcptr) (int);
funcptr f (int x) {
funcptr g;
g = ^(int y) {
printf ("x = %d, y = %d\n", x, y); // use x from enclosing defn
};
g = Block_copy (g);
g (1); // OK, f's activation record still allocated
return g;
}
int main (void) {
funcptr h = f (10);
h (2); // OK, because of Block_copy
f (20);
h (3); // OK, because of Block_copy
Block_release (h);
}
$ sudo apt-get install libblocksruntime-dev
$ clang -fblocks nested-clang.c -lBlocksRuntime
$ ./a.out
x = 10, y = 1
x = 10, y = 2
x = 20, y = 1
x = 10, y = 3 <- safe to call h, created with x=10, GOOD!
Block_copy
and Block_release
$ clang -fblocks nested-clang.c -lBlocksRuntime
$ ./a.out
x = 10, y = 1
x = -1035955720, y = 2 <- unsafe to call h, created with x=10, BAD!
x = 20, y = 1
x = -1035955720, y = 3 <- unsafe to call h, created with x=10, BAD!
func f (_ x:Int) -> (Int) -> () {
func g (_ y:Int) -> () { print ("x = " + String(x) + " y = " + String(y)) }
g (1)
return g
}
func main () {
let h = f (10)
h (2)
let _ = f (20)
h (3)
}
main()
$ swiftc nested-swift.swift
$ ./nested-swift
x = 10 y = 1
x = 10 y = 2
x = 20 y = 1
x = 10 y = 3
def f (x:Int) : Int=>Unit =
def g (y:Int) : Unit = println ("x = %d, y = %d".format (x, y))
g (1)
g
def main () =
val h = f (10)
h (2)
f (20)
h (3)
main()
x = 10, y = 1
x = 10, y = 2
x = 20, y = 1
x = 10, y = 3 <- safe to call h, created with x=10, GOOD!
import java.util.function.IntConsumer;
public class NestedFunc1 {
static IntConsumer f (int x) {
IntConsumer g = y -> System.out.format ("x = %d, y = %d%n", x, y);
g.accept (1);
return g;
}
public static void main (String[] args) {
IntConsumer h = f (10);
h.accept (2);
f (20);
h.accept (3);
}
}
$ javac NestedFunc1.java
$ java NestedFunc1
x = 10, y = 1
x = 10, y = 2
x = 20, y = 1
x = 10, y = 3 <- safe to call h, created with x=10, GOOD!
javac
requires final i
from enclosing scope
for (int i = 0; i < 5; i++) {
new Thread (new Runnable () {
public void run () { /* rejected: i mutates */
while (true) { System.out.print (i); }
}
}).start ();
}
for (int i = 0; i < 5; i++) {
int x = i;
new Thread (new Runnable () {
public void run () { /* accepted: x never mutates */
while (true) { System.out.print (x); }
}
}).start ();
}
for (int i = 0; i < 5; i++) {
new Thread (() -> { /* rejected: i mutates */
while (true) { System.out.print (i); }
}).start ();
}
for (int i = 0; i < 5; i++) {
int x = i;
new Thread (() => { /* accepted: x never mutates */
while (true) { System.out.print (x); }
}).start ();
}
var i = 1
while i < 5 do
Thread (() =>
while true do print (i)
).start
i = i + 1
var i = 1
while i < 5 do
val x = i
Thread (() =>
while true do print (x)
).start
i = i + 1
def main(args: Array[String]): Unit =
for i <- (1 to 4) do
Thread (() =>
while true do print (i)
).start
outer
is called
x
outer
returns nested function inner
x
from outer
's AR
outer
's AR and x
ends
inner
is called
x
from outer
's AR
def outer (x:A) : B=>C =
def inner (y:B) : C =
//...use x and y...
inner
inner
x
def outer (x:A) : B=>C =
def inner (y:B) : C =
...use x and y...
inner
inner
x
and u
inner
sees updated u
?
u
to be immutable?
def outer (x:A) : B=>C =
var u:A = x
def inner (y:B) : C =
//...use u and y...
u = u + 1
inner
u
inner
x
u
(on heap)
def outer (x:A) : B=>C =
var u:A = x
def inner (y:B) : C =
//...use u and y...
u = u + 1
inner
object Closure:
def outer (x:Int) : Boolean=>Int =
def inner (y:Boolean) : Int =
x + (if y then 0 else 1)
inner
$ scalac Closure.scala
$ ls -1 Closure*
Closure$$anonfun$outer$1.class
Closure.class
Closure$.class
Closure.scala
x
copied into field x$1
$ javap -p Closure
Compiled from "Closure.scala"
public final class Closure {
public static scala.Function1<java.lang.Object, java.lang.Object> outer(int);
}
$ javap -p Closure\$\$anonfun\$outer\$1
Compiled from "Closure.scala"
public final class Closure$$anonfun$outer$1 extends scala.runtime.AbstractFunction1<java.lang.Object, java.lang.Object> {
private final int x$1;
public final int apply(boolean);
public Closure$$anonfun$outer$1(int);
}
u
is a var
declaration, so is mutable
object Closure:
def outer (x:Int) : Boolean=>Int =
var u:Int = x
def inner (y:Boolean) : Int =
x + u + (if y then 0 else 1)
inner
x
copied into field x$1
u
shared on heap via reference in field u$1
$ javap -p Closure\$\$anonfun\$outer\$1
Compiled from "Closure.scala"
public final class Closure$$anonfun$outer$1 extends scala.runtime.AbstractFunction1<java.lang.Object, java.lang.Object> {
private final int x$1;
private final scala.runtime.IntRef u$1;
public final int apply(boolean);
public Closure$$anonfun$outer$1(int, scala.runtime.IntRef);
}
x
copied into field x$1
$ javap -p Closure
Compiled from "Closure.scala"
public final class Closure {
public static scala.Function1<java.lang.Object, java.lang.Object> outer(int);
}
$ javap -p Closure\$\$anonfun\$outer\$1
Compiled from "Closure.scala"
public final class Closure$$anonfun$outer$1 extends scala.runtime.AbstractFunction1<java.lang.Object, java.lang.Object> {
private final int x$1;
public final int apply(boolean);
public Closure$$anonfun$outer$1(int);
}