# Independent examples of lexical cells, fresh function identities and defaults.
def make_incrementor(n):
    return lambda x: x + n
inc = make_incrementor(42)
other = make_incrementor(9)
print(inc(0), inc(1), other(1), inc is inc, inc == other)
print(callable(inc), bool(inc), isinstance(inc, object), type(inc).__name__, inc.__name__, inc.__module__)

def counters(start):
    count = start
    def add(amount=1):
        nonlocal count
        count += amount
        return count
    def read():
        return count
    count += 10
    return add, read
add, read = counters(2)
add_other, read_other = counters(100)
print(read(), add(), read(), add(4), read_other(), add_other(2), read())

def snapshots():
    value = 3
    def read(saved=value):
        return saved, value
    value = 8
    return read
print(snapshots()())

def defaults(seed):
    def append(value, items=[seed]):
        items.append(value)
        return items
    return append
left = defaults(1)
right = defaults(9)
print(left(2), right(3), left(4))
print(left.__name__, left == left, left is right)

def callbacks():
    items = []
    for index in range(3):
        items.append(lambda: index)
    return items
saved = callbacks()
print(saved[0](), saved[1](), saved[2](), saved[0] is saved[1])

def outer(value):
    def middle(step):
        def inner():
            nonlocal value
            value += step
            return value
        return inner
    return middle
middle = outer(10)
step_two = middle(2)
step_five = middle(5)
print(step_two(), step_five(), step_two())

class Box:
    def __init__(self, value):
        self.callback = make_incrementor(value)
box = Box(7)
print(box.callback(4), min([-4, 1, -2], key=lambda n: abs(n)))

def exceptions(value):
    def fail():
        nonlocal value
        value += 1
        raise ValueError('closure failure')
    def read():
        return value
    return fail, read
fail, read_failure = exceptions(5)
try:
    fail()
except ValueError as error:
    print(str(error), read_failure())

print((lambda a, /, *extra, scale=2, **kw: (a + sum(extra)) * scale + kw['offset'])(1, 2, 3, offset=4))

def no_capture():
    def empty():
        pass
    return empty
print(no_capture()(), no_capture() is no_capture())

# Comprehension iteration variables must not overwrite an outer captured cell.
def shadow():
    index = 7
    reader = lambda: index
    values = [index for index in range(3)]
    return reader(), values
print(shadow())

# A captured receiver keeps its instance alive through the existing object heap.
class Receiver:
    def __init__(self, value):
        self.value = value
    def callback(self):
        return lambda extra: self.value + extra
receiver = Receiver(20)
callback = receiver.callback()
receiver.value = 30
receiver = Receiver(100)
print(callback(2))

def specialize(value):
    return lambda: value
integer = specialize(12)
text = specialize('text')
print(integer(), text())

selected = make_incrementor(1)
def argument():
    global selected
    selected = make_incrementor(100)
    return 3
print(selected(argument()), selected(3))

def recursive(seed):
    def factorial(n):
        if n == 0:
            return seed
        return n * factorial(n - 1)
    return factorial
print(recursive(2)(5))

# A global declaration in an intermediate scope must stop outer capture.
value = 50
def lexical_global():
    value = 5
    def middle():
        global value
        def inner():
            return value
        return inner
    return middle
print(lexical_global()()())

# Shared Object carriers must not collapse different recursive target types.
def indirect_factory(seed):
    def recur(n):
        if n == 0:
            return seed
        return recur(n - 1)
    def indirect():
        return recur(2)
    return indirect
number_reader = indirect_factory(1)
text_reader = indirect_factory('a')
print(number_reader() + 1, text_reader() + 'b')
