~nch/glue

ref: 50333171e5b5973cdef9c6e8d045750a3a0936e6 glue/arrowlets.py -rw-r--r-- 4.6 KiB
50333171 — nc WIP this seems(?) to be working 1 year, 6 months ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
from typing import Callable, Any
from dataclasses import dataclass

class _CpsA: # (x, k) -> ()
    @staticmethod
    def lift(f) -> '_CpsA':
        return _CpsA(lambda x, k: k(f(x)))

    def __init__(self, cps: Callable[[Any, Callable], None]):
        self.cps = cps

    def next(self, g):
        g = CpsA(g)
        # Basically cons-ing onto the next function functiong
        # (we build the stack backwards so when we pass `x` in during the run it gets
        # sent through the entire stack)
        # In other words, CPS can be viewed as turning a sequence of funcalls into a linked list
        return _CpsA(lambda x, k: self.cps(x, lambda y: g.cps(y, k)))

    def run(self, x):
        return self.cps(x, lambda y: y)

def CpsA(f):
    if isinstance(f, _CpsA): # identity
        return f
    if callable(f): # function
        return _CpsA.lift(f) # lifted into CpsA
    assert False, f


class SimpleEventA(_CpsA):
    def __init__(self, eventname):
        self.eventname = eventname
        def _f(target, k):
            def handler(event):
                # might have to try/catch unbind
                target.unbind(eventname, handler)
                k((target, event))
            target.bind(eventname, handler)
        super().__init__(_f)

def AsyncA(f):
    if isinstance(f, _AsyncA):
        return f
    if callable(f): # function
        return _AsyncA.lift(f)
    assert False, f

@dataclass
class Repeat:
    value: Any

@dataclass
class Done:
    value: Any

class _AsyncA: # (x, p, k) -> ()
    def __init__(self, cps):
        self.acps = cps

    def next(self, g):
        g = AsyncA(g)
        def inner(x, p, k):
            self.acps(x, p, lambda y, q: g.acps(y, q, k))
        return _AsyncA(inner)

    def run(self, x, p=None) -> 'ProgressA':
        p = p if p else ProgressA()
        self.acps(x, p, lambda y, p: y)
        return p

    def repeat(self):
        def repeatf(x, p, k):
            def inner_repeatf(y, q):
                if isinstance(y, Repeat):
                    repeatf(y.value, q, k)
                elif isinstance(y, Done):
                    k(y.value, q)
                else:
                    raise TypeError("Unknown type to repeat on", type(y))
            self.acps(x, p, inner_repeatf)
        return _AsyncA(repeatf)

    def product(self, g):
        g = AsyncA(g)
        def productf(x, p, k):
            c = [2]
            out = [None, None]
            def set_out(i, y):
                out[i] = y
                # barrier
                c[0] -= 1
                if c[0] == 0:
                    k((out[0], out[1]), p)
            self.next(lambda y1: set_out(0, y1)).run(x[0], p)
            g.next(lambda y2: set_out(1, y2)).run(x[1], p)
        return _AsyncA(productf)

    def or_(self, g):
        g = AsyncA(g)
        def or_f(x, p, k):
            prog = [ProgressA(), ProgressA()]
            def end(i):
                prog[i].cancel()
                prog[i] = None
            prog[0].next(lambda _: end(1)).run(None)
            prog[1].next(lambda _: end(0)).run(None)

            def cancel():
                if prog[0]: prog[0].cancel()
                if prog[1]: prog[1].cancel()

            def join(y, q):
                p.advance(cancel)
                k(y, q)

            p.add_canceller(cancel)
            self.acps(x, prog[0], join)
            g.acps(x, prog[1], join)
        return _AsyncA(or_f)

    @staticmethod
    def lift(f: Callable[[Any], Any]):
        return _AsyncA(lambda x, p, k: k(f(x), p))

def ConstA(x):
    return AsyncA(lambda _: x)

class ProgressA(_AsyncA):
    def __init__(self):
        self.cancellers = []
        self.observers = []
        super().__init__(lambda x, p, k: self.observers.append(lambda y: k(y, p)))

    def add_canceller(self, canceller):
        self.cancellers.append(canceller)

    def advance(self, canceller):
        try:
            i = self.cancellers.index(canceller)
            del self.cancellers[i]
        except:
            pass
        while self.observers:
            self.observers.pop()(None)

    def cancel(self):
        while self.cancellers:
            self.cancellers.pop()()

class EventA(_AsyncA):
    def __init__(self, eventname):
        self.eventname = eventname
        def inner(target, p, k):
            def cancel():
                # might have to try/catch unbind
                target.unbind(eventname, handler)
            def handler(event):
                p.advance(cancel)
                cancel()
                k((target, event), p)
            p.add_canceller(cancel)
            target.bind(eventname, handler)
        super().__init__(inner)