OSDN Git Service

4af07727c1dfa5776fc5d0a18c88f4aaa6685c26
[joypy/Thun.git] / docs / sphinx_docs / notebooks / The_Four_Operations.rst
1 The Four Fundamental Operations of Definite Action
2 ==================================================
3
4 All definite actions (computer program) can be defined by four
5 fundamental patterns of combination:
6
7 1. Sequence
8 2. Branch
9 3. Loop
10 4. Parallel
11
12 Sequence
13 --------
14
15 Do one thing after another. In joy this is represented by putting two
16 symbols together, juxtaposition:
17
18 ::
19
20     foo bar
21
22 Operations have inputs and outputs. The outputs of ``foo`` must be
23 compatible in "arity", type, and shape with the inputs of ``bar``.
24
25 Branch
26 ------
27
28 Do one thing or another.
29
30 ::
31
32     boolean [F] [T] branch
33
34
35        t [F] [T] branch
36     ----------------------
37               T
38
39
40        f [F] [T] branch
41     ----------------------
42           F
43
44
45     branch == unit cons swap pick i
46
47     boolean [F] [T] branch
48     boolean [F] [T] unit cons swap pick i
49     boolean [F] [[T]] cons swap pick i
50     boolean [[F] [T]] swap pick i
51     [[F] [T]] boolean pick i
52     [F-or-T] i
53
54 Given some branch function ``G``:
55
56 ::
57
58     G == [F] [T] branch
59
60 Used in a sequence like so:
61
62 ::
63
64     foo G bar
65
66 The inputs and outputs of ``F`` and ``T`` must be compatible with the
67 outputs for ``foo`` and the inputs of ``bar``, respectively.
68
69 ::
70
71     foo F bar
72
73     foo T bar
74
75 ``ifte``
76 ~~~~~~~~
77
78 Often it will be easier on the programmer to write branching code with
79 the predicate specified in a quote. The ``ifte`` combinator provides
80 this (``T`` for "then" and ``E`` for "else"):
81
82 ::
83
84     [P] [T] [E] ifte
85
86 Defined in terms of ``branch``:
87
88 ::
89
90     ifte == [nullary not] dip branch
91
92 In this case, ``P`` must be compatible with the stack and return a
93 Boolean value, and ``T`` and ``E`` both must be compatible with the
94 preceeding and following functions, as described above for ``F`` and
95 ``T``. (Note that in the current implementation we are depending on
96 Python for the underlying semantics, so the Boolean value doesn't *have*
97 to be Boolean because Python's rules for "truthiness" will be used to
98 evaluate it. I reflect this in the structure of the stack effect comment
99 of ``branch``, it will only accept Boolean values, and in the definition
100 of ``ifte`` above by including ``not`` in the quote, which also has the
101 effect that the subject quotes are in the proper order for ``branch``.)
102
103 Loop
104 ----
105
106 Do one thing zero or more times.
107
108 ::
109
110     boolean [Q] loop
111
112
113        t [Q] loop
114     ----------------
115        Q [Q] loop
116
117
118        ... f [Q] loop
119     --------------------
120        ...
121
122 The ``loop`` combinator generates a copy of itself in the true branch.
123 This is the hallmark of recursive defintions. In Thun there is no
124 equivalent to conventional loops. (There is, however, the ``x``
125 combinator, defined as ``x == dup i``, which permits recursive
126 constructs that do not need to be directly self-referential, unlike
127 ``loop`` and ``genrec``.)
128
129 ::
130
131     loop == [] swap [dup dip loop] cons branch
132
133     boolean [Q] loop
134     boolean [Q] [] swap [dup dip loop] cons branch
135     boolean [] [Q] [dup dip loop] cons branch
136     boolean [] [[Q] dup dip loop] branch
137
138 In action the false branch does nothing while the true branch does:
139
140 ::
141
142     t [] [[Q] dup dip loop] branch
143           [Q] dup dip loop
144           [Q] [Q] dip loop
145           Q [Q] loop
146
147 Because ``loop`` expects and consumes a Boolean value, the ``Q``
148 function must be compatible with the previous stack *and itself* with a
149 boolean flag for the next iteration:
150
151 ::
152
153     Q == G b
154
155     Q [Q] loop
156     G b [Q] loop
157     G Q [Q] loop
158     G G b [Q] loop
159     G G Q [Q] loop
160     G G G b [Q] loop
161     G G G
162
163 ``while``
164 ~~~~~~~~~
165
166 Keep doing ``B`` *while* some predicate ``P`` is true. This is
167 convenient as the predicate function is made nullary automatically and
168 the body function can be designed without regard to leaving a Boolean
169 flag for the next iteration:
170
171 ::
172
173                 [P] [B] while
174     --------------------------------------
175        [P] nullary [B [P] nullary] loop
176
177
178     while == swap [nullary] cons dup dipd concat loop
179
180
181     [P] [B] while
182     [P] [B] swap [nullary] cons dup dipd concat loop
183     [B] [P] [nullary] cons dup dipd concat loop
184     [B] [[P] nullary] dup dipd concat loop
185     [B] [[P] nullary] [[P] nullary] dipd concat loop
186     [P] nullary [B] [[P] nullary] concat loop
187     [P] nullary [B [P] nullary] loop
188
189 Parallel
190 --------
191
192 The *parallel* operation indicates that two (or more) functions *do not
193 interfere* with each other and so can run in parallel. The main
194 difficulty in this sort of thing is orchestrating the recombining
195 ("join" or "wait") of the results of the functions after they finish.
196
197 The current implementaions and the following definitions *are not
198 actually parallel* (yet), but there is no reason they couldn't be
199 reimplemented in terms of e.g. Python threads. I am not concerned with
200 performance of the system just yet, only the elegance of the code it
201 allows us to write.
202
203 ``cleave``
204 ~~~~~~~~~~
205
206 Joy has a few parallel combinators, the main one being ``cleave``:
207
208 ::
209
210                    ... x [A] [B] cleave
211     ---------------------------------------------------------
212        ... [x ...] [A] infra first [x ...] [B] infra first
213     ---------------------------------------------------------
214                        ... a b
215
216 The ``cleave`` combinator expects a value and two quotes and it executes
217 each quote in "separate universes" such that neither can affect the
218 other, then it takes the first item from the stack in each universe and
219 replaces the value and quotes with their respective results.
220
221 (I think this corresponds to the "fork" operator, the little
222 upward-pointed triangle, that takes two functions ``A :: x -> a`` and
223 ``B :: x -> b`` and returns a function ``F :: x -> (a, b)``, in Conal
224 Elliott's "Compiling to Categories" paper, et. al.)
225
226 Just a thought, if you ``cleave`` two jobs and one requires more time to
227 finish than the other you'd like to be able to assign resources
228 accordingly so that they both finish at the same time.
229
230 "Apply" Functions
231 ~~~~~~~~~~~~~~~~~
232
233 There are also ``app2`` and ``app3`` which run a single quote on more
234 than one value:
235
236 ::
237
238                      ... y x [Q] app2
239      ---------------------------------------------------------
240         ... [y ...] [Q] infra first [x ...] [Q] infra first
241
242
243             ... z y x [Q] app3
244      ---------------------------------
245         ... [z ...] [Q] infra first
246             [y ...] [Q] infra first
247             [x ...] [Q] infra first
248
249 Because the quoted program can be ``i`` we can define ``cleave`` in
250 terms of ``app2``:
251
252 ::
253
254     cleave == [i] app2 [popd] dip
255
256 (I'm not sure why ``cleave`` was specified to take that value, I may
257 make a combinator that does the same thing but without expecting a
258 value.)
259
260 ::
261
262     clv == [i] app2
263
264        [A] [B] clv
265     ------------------
266          a b
267
268 ``map``
269 ~~~~~~~
270
271 The common ``map`` function in Joy should also be though of as a
272 *parallel* operator:
273
274 ::
275
276     [a b c ...] [Q] map
277
278 There is no reason why the implementation of ``map`` couldn't distribute
279 the ``Q`` function over e.g. a pool of worker CPUs.
280
281 ``pam``
282 ~~~~~~~
283
284 One of my favorite combinators, the ``pam`` combinator is just:
285
286 ::
287
288     pam == [i] map
289
290 This can be used to run any number of programs separately on the current
291 stack and combine their (first) outputs in a result list.
292
293 ::
294
295        [[A] [B] [C] ...] [i] map
296     -------------------------------
297        [ a   b   c  ...]
298
299 Handling Other Kinds of Join
300 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
301
302 The ``cleave`` operators and others all have pretty brutal join
303 semantics: everything works and we always wait for every
304 sub-computation. We can imagine a few different potentially useful
305 patterns of "joining" results from parallel combinators.
306
307 first-to-finish
308 ^^^^^^^^^^^^^^^
309
310 Thinking about variations of ``pam`` there could be one that only
311 returns the first result of the first-to-finish sub-program, or the
312 stack could be replaced by its output stack.
313
314 The other sub-programs would be cancelled.
315
316 "Fulminators"
317 ^^^^^^^^^^^^^
318
319 Also known as "Futures" or "Promises" (by *everybody* else. "Fulinators"
320 is what I was going to call them when I was thinking about implementing
321 them in Thun.)
322
323 The runtime could be amended to permit "thunks" representing the results
324 of in-progress computations to be left on the stack and picked up by
325 subsequent functions. These would themselves be able to leave behind
326 more "thunks", the values of which depend on the eventual resolution of
327 the values of the previous thunks.
328
329 In this way you can create "chains" (and more complex shapes) out of
330 normal-looking code that consist of a kind of call-graph interspersed
331 with "asyncronous" ... events?
332
333 In any case, until I can find a rigorous theory that shows that this
334 sort of thing works perfectly in Joy code I'm not going to worry about
335 it. (And I think the Categories can deal with it anyhow? Incremental
336 evaluation, yeah?)