OSDN Git Service

More update to 4.3.0
[joypy/Thun.git] / docs / Document.md
1
2 # Joy
3
4 This document is written to capture, at least crudely, the scope of application for Joy and the Joypy implementation.  It kind of expects that you have some familiarity with Joy already.
5
6 It is vaguely organized, in a pile.
7
8
9 ## Syntax
10
11 Very simple syntax.  Could be specified as a sequence of one or more terms:
12
13     joy ::= term term*
14
15 Conceptually, all terms are unary functions `F :: stack -> stack` that accept a stack and return a stack.  But we immediately differentiate between literals (of a few kinds), functions, and combinators (which like higher-order functions.)
16
17
18 ### In Joypy there are currently four literal types.
19
20 First we have the types borrowed from the underlying Python semantics. **Strings** (byte and Unicode with nuances depending on whether you're running under Python 2 or 3), **ints**, and **floats**.  Then there is the **sequence** type, aka "quote", "list", etc...  In joy it is represented by enclosing zero or more terms in square brackets:
21
22     sequence :== '[' term* ']'
23
24 (In Joypy it is implemented as a cons-list.  All datastructures in Joypy are built out of this single sequence type, including the stack and expression.  I could include Python `frozenset` but I don't.)
25
26     literal ::= string | int | float | sequence
27
28 Functions accept zero or more arguments from the stack and push back zero or more results.
29
30 Combinators are functions one or more of the arguments to which are quotes containing joy expressions, and which then execute one or more of their quoted arguments to effect their function.
31
32     term ::= literal | function | combinator
33
34 The code for the parser is in `joy/parser.py`.
35
36
37 ## Semantics
38
39 In Joy juxtaposition of symbols is composition of functions.  That means that `F G` syntactically is `G(F(...))` semantically.
40
41 As it says in the [Wikipedia entry for Joypy](https://en.wikipedia.org/wiki/Joy_%28programming_language%29):
42
43     "In Joy, the meaning function is a homomorphism from the syntactic monoid onto the semantic monoid. That is, the syntactic relation of concatenation of symbols maps directly onto the semantic relation of composition of functions."
44
45 Isn't that nice?
46
47
48 ## Joypy Continuation-Passing Style
49
50 In Joypy all the combinators work by modifying the pending expression.  We have enlarged the definition of function to be from a two-tuple of `(stack, expression)` to another such two-tuple:
51
52     F :: (stack, expression) -> (stack, expression)
53
54 Simple functions ignore the expression and pass it through unchanged, combinators do not.  They can modify it and this is enough to define control-flow and other operators.
55
56 (Actually...  In Joypy the functions all also include a dictionary parameter.  This allows for functions like `print_words` and `help`.  It also allows for the definition of a `define` function which would let Joy code add new definitions to the dictionary during evaluation, but this is an area I am leaving unexplored at least for now.  It is essentially name-binding (variables) sneaking in, breaking the purity of the system.)
57
58
59 ## Evaluation
60
61 The joy interpreter is a very simple loop.  As long as the expression is non-empty the interpreter pops the next term and checks it, if it's a literal it's pushed onto the stack, if it's a function or combinator the interpreter calls it passing the current stack and expression, which are then replaced by whatever the function or combinator returns.
62
63 There is no call stack.  All state is kept either on the stack or in the pending expression.  At each interpreter iteration the stack and expression are complete.  (They can be pickled, saved to disc or sent over the network, and reconstituted at any time, etc...)
64
65
66 # Methods of Meta-programming
67
68 Joy seems to lend itself to several complementary forms of meta-programming to develop more-efficient versions of functions.
69
70
71 ## Compiling definitions.
72
73 Due to the fact that "juxtaposition of symbols is composition of functions" the *simplest* way to "compile" the Joy expression `F G` would be the Python expression:
74
75     lambda s, e, d: G(*F(s, e, d))
76
77 This produces a new unnamed function that delivers the output of `F` directly to `G` without passing back through the interpreter loop.
78
79 If we wanted to do more work than that, we could inspect the bytecode of the two Python functions, figure out how they name their arguments, and attempt to produce new bytecode that corresponds to the composition of them.  This is a little beyond me at the moment, but it's not unrealistic given enough time and attention.
80
81 It will usually be easier to manually write new custom words. For example, the "plus or minus" operator `pm`, defined as:
82
83     pm == [+] [-] cleave popdd
84
85 Can be implemented in Python as:
86
87     @SimpleFunctionWrapper
88     def pm(stack):
89         a, (b, stack) = stack
90         p = b + a
91         m = b - a
92         return m, (p, stack)
93
94 Code that uses `pm` will will work the same but more quickly if the "compiled" version is inscribed in the dictionary.
95
96 It would be remiss not to mention **Cython** in this connection.  Many Joy functions can be transparently compiled down to machine code.
97
98 Beyond the above, it should be possible to make use of much of the existing body of knowledge for compiling *functional programming* languages to machine code for making an actual Joy compiler.  Joy omits many "features" that are common to most other languages, lambda abstraction and `let` statements for example.  I have not had the time to investigate compilation of Joy in any depth so far, but I have high hopes.  It should be possible (and most of the details will have been already worked out in other languages) to go from e.g. the definition form of `pm` to the Python form automatically.
99
100
101 ## Partial Evaluation
102
103 Cf. "Futamura projections"
104
105 ["partial evaluation is a technique for several different types of program optimization by specialization. The most straightforward application is to produce new programs which run faster than the originals while being guaranteed to behave in the same way."](https://en.wikipedia.org/wiki/Partial_evaluation) ~Wikipedia
106
107 Given a function and some (but not all) of its arguments you can run the interpreter in a speculative fashion and derive new functions that are specializations of the original.
108
109 Example from [Futamura, 1983](https://repository.kulib.kyoto-u.ac.jp/dspace/bitstream/2433/103401/1/0482-14.pdf) of converting a power function to a "to the fifth power" function:
110
111     F(k, u) -> u^k
112
113 I like to use a kind of crude [Gentzen notation](https://en.wikipedia.org/wiki/Natural_deduction) to describe a Joy function's semantics:
114
115        k u F
116     -----------
117         u^k
118
119 Joy function implementation:
120
121     F == 1 [popop 0 !=] [[popop 2 %] [over *] [] ifte [1 >>] dipd [sqr] dip] while [popop] dip
122
123 This is a bit longer than a definition should be.  In practice I would refactor it to be more concise and easily understood.
124
125 In Python for comparison:
126
127     def power(k, u):
128         z = 1
129         while k != 0:
130             if k % 2:
131                 z = z * u
132             k = k >> 1
133             u = u * u
134         return z
135
136 Using 5 for `k` and pushing evaluation forward as far as it will go with a sort of "thunk" variable for `u` we arrive at:
137
138     u u u * dup * *
139
140 We can replace the extra occurrences of `u` with `dup` to arrive at a definition for a Joy function that, given a number on the stack, returns that number raised to the fifth power:
141
142     to-the-fifth == dup dup * dup * *
143
144 Here it is in action:
145
146     u dup dup * dup * *
147     u u   dup * dup * *
148     u u u     * dup * *
149     u u^2       dup * *
150     u u^2 u^2       * *
151     u u^4             *
152     u^5
153
154 See the appendix below for the derivation of the specialized form from the general form.
155
156 It should be possible to write a program `FutamuraI` that works like this:
157
158         [5] [F] FutamuraI
159     -------------------------
160        [dup dup * dup * *]
161
162
163 That is, given the quoted program `[F]` and the argument `5`, it returns the new `to-the-fifth` function in quoted form.
164
165
166 ### First Futamura Projection
167
168 A joy interpreter written in Joy is described in the literature (available from the La Trobe archive or the mirror site) so we can apply the program `FutamuraI` to that to get a *residual* program `R` for some program `Q`:
169
170        [Q] [joy] FutamuraI
171     -------------------------
172               [R]
173
174 The expected result is that, for a given input, the runtime of `R` is less than or equal to the runtime of `Q`.
175
176 If we had a partial evaluator for Python we could create a residual program in Python for the Joy program `Q`.
177
178
179 ### Second Futamura Projection
180
181        [joy] [FutamuraI] FutamuraI
182     ---------------------------------
183                 [C]
184
185 Making a compiler by "specializing the specializer for the interpreter".
186
187
188 ### Third Futamura Projection
189
190        [FutamuraI] [FutamuraI] FutamuraI
191     ---------------------------------------
192                      [K]
193
194 "Specializing the specializer for itself yielding a tool that can convert any interpreter to an equivalent compiler"
195
196        [joy] K
197     -------------
198          [C]
199
200
201
202        [Q] [joy] K i 
203     -------------------
204            [Q] C
205         -----------
206             [R]
207
208
209
210
211     [K] K -> [K]
212
213
214
215
216 ## Super-Compilation
217
218 https://en.wikipedia.org/wiki/Metacompilation
219
220 https://themonadreader.files.wordpress.com/2014/04/super-final.pdf
221
222 This is a little hard to describe succinctly, but you are basically trying to figure out all possible paths through a program and then use that knowledge to improve the code, somehow.  (I forget the details, but it's worth including and revisiting.)
223
224
225 ## Gödel Machine
226
227 http://people.idsia.ch/~juergen/goedelmachine.html
228
229 https://en.wikipedia.org/wiki/G%C3%B6del_machine
230
231 In Joy it often happens that a new general form is discovered that is semantically equivalent to some other form but that has greater efficiency (at least under some definite conditions.)  When this happens we can perform a kind of search-and-replace operation over the whole of the current dictionary (standard library in other languages) and achieve performance gains.
232
233 As an example, the function `[1 >>] dipd [sqr] dip` can be rewritten as `[[1 >>] dip sqr] dip` which, depending on the other optimizations some interpreter might make, could be more efficient.  We can generalize this to a pattern-matching rule, something like:
234
235     [F] dipd [G] dip == [[F] dip G] dip
236
237 And we are justified rewriting any occurrence of the pattern on either side to the other if it improves things.
238
239 The above also suggests a new combinator, call it `dipdip` that abstracts the pattern:
240
241        ... a b [F] [G] dipdip
242     ----------------------------
243            ... F a G b
244
245 This permits the compiler to make optimizations without having to work to notice the pattern.  The `dipdip` function and the interpreter can work together to do the more efficient thing.
246
247 Joy function definitions form Directed Graphs.  Not acyclical though, definition bodies do not contain references to other functions, but rather "Symbols" that name functions, so you can form e.g. two definitions that each make use of the other.  Generally speaking though, you don't do this, instead you write definitions that use e.g. `genrec` general recursion combinator. 
248
249 Anyway, because Joy code is just a graph it becomes pretty easy to rewrite the graph in ways that preserve the semantics but are more efficient.  Doing this in an automated fashion is essentially Schmidhuber's Gödel Machine:  Finding and applying provably-correct modifications to the whole system in a self-referential way to create a self-improving general problem solver.
250
251 Joy is intended as an effective vehicle for exploring this potential.
252
253
254 ## Speculative pre-evaluation
255
256 If you examine the traces of Joy programs it's easy to find places in the pending expression where some speculative interpreter could pre-compute results while the main interpreter was prosecuting the main "thread" of the program.  For example consider (with the `.` indicating the current "location of the interpreter head" if you will, the split between the stack and the expression):
257
258     ... a b c . F 2 3 + G H
259
260 The `2 3 +` between `F` and `G` is not at the interpreter "head" yet it is extremely unlikely that any function `F` will prevent it (eventually) being evaluated to `5`.  We can imagine an interpreter that detects this sort of thing, evaluates the sub-expression with a different CPU, and "tags" the expression at `2` with the result `5`.  If evaluation reaches `2` the interpreter can just use `5` without re-evaluating the whole sub-expression `2 3 +`.
261
262 This sort of thing happens all the time in Joy code.
263
264 For example, if you look at the appendix for the partial evaluation example there is a stage where we have this:
265
266     5 u u [1 >>] dipd [sqr] dip
267
268 Which can be written with the `dipdip` combinator:
269
270     5 u u [1 >>] [sqr] dipdip
271
272 Which then becomes this:
273
274     5 1 >> u sqr u
275
276 The interpreter could notice that `5 1 >>` and `u sqr` can proceed in parallel without interfering with each other.  The `dipdip` combinator could be written to somehow hint to the interpreter that it should check for this posibility.
277
278
279 ## JIT
280
281 Whatever eventually winds up converting Joy code to machine code is susceptible to Just-in-Time compilation.  For example, if you run Joypy on Pypy you take advantage of its JIT.
282
283
284 # Joy as UI
285
286
287 ## Joy unifies CLI and GUI interfaces.
288
289 All Joy interaction consists of two basic actions:
290
291 1. Putting things onto the stack.
292 2. Executing functions.
293
294 In a command-line setting you perform both of these actions the same way: entering Joy expressions as text.  In a GUI you select items and copy or cut them to a user-visible stack (that is a first-class member of the UI, similar to the clipboard but with better visibility into contents and not restricted to one selection at a time.)  You then trigger the evaluation of functions by clicking on buttons or menu items.  *From the point-of-view of the underlying interpreter there is no difference between the input token streams for either UI modality.*
295
296
297 ## Simple and Comprehensible Model
298
299 In order to use their system(s) users must be able to easily and quickly develop a mental model of the system that maps to the actual system abstractions well enough to support the achievement of their goals.
300
301 (Arguably current systems are pretty poor at this.  Even an abstraction as old and ubiquitous as "filesystem" is only incompletely understood by many computer users.  Many people do not understand the difference between RAM and disk storage!)
302
303 The Joy model consists of just these main concepts:
304
305 1. A stack of values
306 2. A dictionary of named commands
307 3. An interpreter
308
309 Each of these is very simple and the first two even have real-world analogs (e.g. a *stack* of dishes or boxes or whatever, and, well, *dictionaries*.)  It's easy to develop intuition for this system, resulting in a close match between the user's mental model and the actual system abstraction.
310
311
312 # Joy as AST for multi-language interop
313
314 IR for Compilation
315
316 Cf. Graal & Truffle
317
318 "Software is eating the world"; Joy eats software.
319
320 Universal Solvent
321
322 Can write front-ends for translating other languages into Joy, thence to be refactored and fulminated into more efficient forms.  "The Blob" of software.
323
324
325 # Minimal Basis
326
327 Cf. SKI combinators, Peano arithmentic, Church numerals et. al.,
328
329 Folks have done work on figuring out the minimal set of combinators that are Turing-complete.  Several of these sets are quite small.
330
331 Semantics can be defined in terms of Laws of Form for down-to-the-metal modeling of programs as logic circuits.  Hardware description language.
332
333
334
335 # Math, Physics, Computation
336
337     Computational algorithms are used to communicate precisely
338     some of the methods used in the analysis of dynamical phenomena.
339     Expressing the methods of variational mechanics in a computer
340     language forces them to be unambiguous and computationally
341     effective. Computation requires us to be precise about the repre-
342     sentation of mechanical and geometric notions as computational
343     objects and permits us to represent explicitly the algorithms for
344     manipulating these objects. Also, once formalized as a procedure,
345     a mathematical idea becomes a tool that can be used directly to
346     compute results.
347        - "Structure and Interpretation of Classical Mechanics",
348        Gerald Jay Sussman and Jack Wisdom with Meinhard E. Mayer
349
350 .
351
352
353
354 # Joy as glue language
355
356 Basically any existing code/programs can be exposed to Joy as a function or collection of functions.
357
358 ## Shell command
359
360 Run a shell command.
361
362         "stdin" "cmd line" system
363     -----------------------------------
364        "stderr" "stdout" return_code
365
366 Then you can create e.g.:
367
368     foo == "awk {awk program}" system
369
370 Etc...
371
372 ## Python libraries
373
374 ## Ctypes (FFI) for loading binary libraries
375
376
377
378
379 # Git as File Store
380
381 The old-fashioned File System abstraction is no longer justified.  Joypy won't attempt to implement file and path operations.  Instead there are a few functions that accept three args: a sha1 checksum of a blob of data, an initial index, and an offset.  One function returns the string of data `blob[index:index+offset]`, while another accepts an additional quoted program and "runs it" with the data as the stack, for when you want to process a big ol' pile of data but don't want to load it into the interpreter.  I imagine a use case for a third-party wrapped library that expects some sort of file or socket and streams over it somehow.  Obviously, this is under-specified.
382
383 The sha1 checksum refers to data stored in some (global, universal) git repo, which is provided to the interpreter though some as-yet unimplemented meta-interpreter action.
384
385 **Git is a functional data type**, compatible with the semantic model of Joy.  Implies shared datastore with obvious connection to git-archive & Datalad.
386
387 Functions over static data (Wikipedia dump; MRI data &c.) can be considered timeless (however much time their first evaluation takes) and cached/archived in the global shared git repo.  (Large data in e.g. cloud & bittorrent, with meta-data in git-archive/Datalad)
388
389 Functions over streams (of possible mal-formed) data require a special stream-processing combinator and more care in their development.  I haven't developed this in any detail, but it can be shown in many cases that e.g. a given function cannot grow unbounded (for all possible unbounded input streams.)
390
391
392
393 # Sympy Library
394
395 The mathematical functions in the Joypy library wrap the `math` module and other built-ins for the most part.  It would be a simple matter to write wrapper functions for e.g. the Sympy packages' functions and provide symbolic math capabilities.
396
397 It would also be possible to make a dictionary that mapped the math functions to the Sympy versions.  Evaluating Joy code with this dictionary (and a special stack with Sympy variables on it) would result in symbolic execution without rewriting the Joy code.
398
399
400
401 # Stack-based laguages as Dataflow
402
403 If the "places" in a stack are considered first-class entities and tracked through "stack chatter" operations (like `swap`) we can draw flow-lines for the data and represent the functions as boxes with input and output lines.  Stack chatter becomes topological rearrangements of lines.  The resulting structure is conceptually identical with *Dataflow* paradigm of programming.
404
405 (Related to this I suspect that all stack chatter disappears during compilation but I haven't nailed that down yet.)
406
407 I'm unable to find the original webpage that describe the above.  :-(
408
409
410 # Appendix Partial Evaluation Example
411
412        k u F
413     -----------
414         u^k
415
416
417     k u 1 [popop 0 !=] [[popop odd][over *][]ifte [1 >>] dipd [sqr] dip] while [popop] dip
418
419     F == 1 [popop 0 !=] [[popop odd][over *][]ifte [1 >>] dipd [sqr] dip] while [popop] dip
420
421     5 u 1 [popop 0 !=] [[popop odd][over *][]ifte [1 >>] dipd [sqr] dip] while [popop] dip
422
423
424     5 u 1 popop 0 !=
425     5           0 !=
426     True
427
428
429     5 u 1 [popop odd][over *][]ifte [1 >>] dipd [sqr] dip
430     5 u 1 popop odd
431     True
432
433     w/ sqr == dup *
434
435     5 u 1 over * [1 >>] dipd [sqr] dip
436     5 u 1 u    * [1 >>] dipd [sqr] dip
437     5 u u        [1 >>] dipd [sqr] dip
438     5 1 >> u sqr u
439     2      u_dup_*   u
440           --or--
441     2      u_u_*   u
442
443     2 u_u_* u popop 0 !=
444     2               0 !=
445     True
446     
447     2 u_u_* u [popop odd][over *][]ifte [1 >>] dipd [sqr] dip
448     ...
449     2 u_u_* u                           [1 >>] dipd [sqr] dip
450
451     2 1 >> u_u_* sqr   u
452     1      u_u_*_dup_* u
453
454
455     1 u_u_*_dup_* u [popop odd][over *][]ifte [1 >>] dipd [sqr] dip
456     1 u_u_*_dup_* u             over *        [1 >>] dipd [sqr] dip
457     1 u_u_*_dup_* u u_u_*_dup_*      *        [1 >>] dipd [sqr] dip
458     1 u_u_*_dup_* u_u_u_*_dup_*_*             [1 >>] dipd [sqr] dip
459
460     1 1 >> u_u_*_dup_* sqr           u_u_u_*_dup_*_*
461     0      u_u_*_dup_* dup *         u_u_u_*_dup_*_*
462     0      u_u_*_dup_* u_u_*_dup_* * u_u_u_*_dup_*_*
463     0      u_..._*                   u_u_u_*_dup_*_*
464
465     0      u_..._*                   u_u_u_*_dup_*_* [popop] dip
466
467     u_u_u_*_dup_*_*
468     
469     ^5 == dup dup * dup * *