OSDN Git Service

Some docs that got swept up in the commotion.
authorSimon Forman <sforman@hushmail.com>
Wed, 6 Jun 2018 20:08:04 +0000 (13:08 -0700)
committerSimon Forman <sforman@hushmail.com>
Wed, 6 Jun 2018 20:08:04 +0000 (13:08 -0700)
docs/Document.md [new file with mode: 0644]
docs/README.md [new file with mode: 0644]

diff --git a/docs/Document.md b/docs/Document.md
new file mode 100644 (file)
index 0000000..5c9387b
--- /dev/null
@@ -0,0 +1,469 @@
+
+# Joy
+
+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.
+
+It is vaguely organized, in a pile.
+
+
+## Syntax
+
+Very simple syntax.  Could be specified as a sequence of one or more terms:
+
+    joy ::= term term*
+
+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.)
+
+
+### In Joypy there are currently four literal types.
+
+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:
+
+    sequence :== '[' term* ']'
+
+(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.)
+
+    literal ::= string | int | float | sequence
+
+Functions accept zero or more arguments from the stack and push back zero or more results.
+
+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.
+
+    term ::= literal | function | combinator
+
+The code for the parser is in `joy/parser.py`.
+
+
+## Semantics
+
+In Joy juxtaposition of symbols is composition of functions.  That means that `F G` syntactically is `G(F(...))` semantically.
+
+As it says in the [Wikipedia entry for Joypy](https://en.wikipedia.org/wiki/Joy_%28programming_language%29):
+
+    "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."
+
+Isn't that nice?
+
+
+## Joypy Continuation-Passing Style
+
+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:
+
+    F :: (stack, expression) -> (stack, expression)
+
+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.
+
+(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.)
+
+
+## Evaluation
+
+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.
+
+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...)
+
+
+# Methods of Meta-programming
+
+Joy seems to lend itself to several complementary forms of meta-programming to develop more-efficient versions of functions.
+
+
+## Compiling definitions.
+
+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:
+
+    lambda s, e, d: G(*F(s, e, d))
+
+This produces a new unnamed function that delivers the output of `F` directly to `G` without passing back through the interpreter loop.
+
+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.
+
+It will usually be easier to manually write new custom words. For example, the "plus or minus" operator `pm`, defined as:
+
+    pm == [+] [-] cleave popdd
+
+Can be implemented in Python as:
+
+    @SimpleFunctionWrapper
+    def pm(stack):
+        a, (b, stack) = stack
+        p = b + a
+        m = b - a
+        return m, (p, stack)
+
+Code that uses `pm` will will work the same but more quickly if the "compiled" version is inscribed in the dictionary.
+
+It would be remiss not to mention **Cython** in this connection.  Many Joy functions can be transparently compiled down to machine code.
+
+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.
+
+
+## Partial Evaluation
+
+Cf. "Futamura projections"
+
+["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
+
+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.
+
+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:
+
+    F(k, u) -> u^k
+
+I like to use a kind of crude [Gentzen notation](https://en.wikipedia.org/wiki/Natural_deduction) to describe a Joy function's semantics:
+
+       k u F
+    -----------
+        u^k
+
+Joy function implementation:
+
+    F == 1 [popop 0 !=] [[popop 2 %] [over *] [] ifte [1 >>] dipd [sqr] dip] while [popop] dip
+
+This is a bit longer than a definition should be.  In practice I would refactor it to be more concise and easily understood.
+
+In Python for comparison:
+
+    def power(k, u):
+        z = 1
+        while k != 0:
+            if k % 2:
+                z = z * u
+            k = k >> 1
+            u = u * u
+        return z
+
+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:
+
+    u u u * dup * *
+
+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:
+
+    to-the-fifth == dup dup * dup * *
+
+Here it is in action:
+
+    u dup dup * dup * *
+    u u   dup * dup * *
+    u u u     * dup * *
+    u u^2       dup * *
+    u u^2 u^2       * *
+    u u^4             *
+    u^5
+
+See the appendix below for the derivation of the specialized form from the general form.
+
+It should be possible to write a program `FutamuraI` that works like this:
+
+        [5] [F] FutamuraI
+    -------------------------
+       [dup dup * dup * *]
+
+
+That is, given the quoted program `[F]` and the argument `5`, it returns the new `to-the-fifth` function in quoted form.
+
+
+### First Futamura Projection
+
+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`:
+
+       [Q] [joy] FutamuraI
+    -------------------------
+              [R]
+
+The expected result is that, for a given input, the runtime of `R` is less than or equal to the runtime of `Q`.
+
+If we had a partial evaluator for Python we could create a residual program in Python for the Joy program `Q`.
+
+
+### Second Futamura Projection
+
+       [joy] [FutamuraI] FutamuraI
+    ---------------------------------
+                [C]
+
+Making a compiler by "specializing the specializer for the interpreter".
+
+
+### Third Futamura Projection
+
+       [FutamuraI] [FutamuraI] FutamuraI
+    ---------------------------------------
+                     [K]
+
+"Specializing the specializer for itself yielding a tool that can convert any interpreter to an equivalent compiler"
+
+       [joy] K
+    -------------
+         [C]
+
+
+
+       [Q] [joy] K i 
+    -------------------
+           [Q] C
+        -----------
+            [R]
+
+
+
+
+    [K] K -> [K]
+
+
+
+
+## Super-Compilation
+
+https://en.wikipedia.org/wiki/Metacompilation
+
+https://themonadreader.files.wordpress.com/2014/04/super-final.pdf
+
+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.)
+
+
+## Gödel Machine
+
+http://people.idsia.ch/~juergen/goedelmachine.html
+
+https://en.wikipedia.org/wiki/G%C3%B6del_machine
+
+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.
+
+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:
+
+    [F] dipd [G] dip == [[F] dip G] dip
+
+And we are justified rewriting any occurrence of the pattern on either side to the other if it improves things.
+
+The above also suggests a new combinator, call it `dipdip` that abstracts the pattern:
+
+       ... a b [F] [G] dipdip
+    ----------------------------
+           ... F a G b
+
+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.
+
+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. 
+
+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.
+
+Joy is intended as an effective vehicle for exploring this potential.
+
+
+## Speculative pre-evaluation
+
+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):
+
+    ... a b c . F 2 3 + G H
+
+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 +`.
+
+This sort of thing happens all the time in Joy code.
+
+For example, if you look at the appendix for the partial evaluation example there is a stage where we have this:
+
+    5 u u [1 >>] dipd [sqr] dip
+
+Which can be written with the `dipdip` combinator:
+
+    5 u u [1 >>] [sqr] dipdip
+
+Which then becomes this:
+
+    5 1 >> u sqr u
+
+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.
+
+
+## JIT
+
+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.
+
+
+# Joy as UI
+
+
+## Joy unifies CLI and GUI interfaces.
+
+All Joy interaction consists of two basic actions:
+
+1. Putting things onto the stack.
+2. Executing functions.
+
+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.*
+
+
+## Simple and Comprehensible Model
+
+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.
+
+(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!)
+
+The Joy model consists of just these main concepts:
+
+1. A stack of values
+2. A dictionary of named commands
+3. An interpreter
+
+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.
+
+
+# Joy as AST for multi-language interop
+
+IR for Compilation
+
+Cf. Graal & Truffle
+
+"Software is eating the world"; Joy eats software.
+
+Universal Solvent
+
+Can write front-ends for translating other languages into Joy, thence to be refactored and fulminated into more efficient forms.  "The Blob" of software.
+
+
+# Minimal Basis
+
+Cf. SKI combinators, Peano arithmentic, Church numerals et. al.,
+
+Folks have done work on figuring out the minimal set of combinators that are Turing-complete.  Several of these sets are quite small.
+
+Semantics can be defined in terms of Laws of Form for down-to-the-metal modeling of programs as logic circuits.  Hardware description language.
+
+
+
+# Math, Physics, Computation
+
+    Computational algorithms are used to communicate precisely
+    some of the methods used in the analysis of dynamical phenomena.
+    Expressing the methods of variational mechanics in a computer
+    language forces them to be unambiguous and computationally
+    effective. Computation requires us to be precise about the repre-
+    sentation of mechanical and geometric notions as computational
+    objects and permits us to represent explicitly the algorithms for
+    manipulating these objects. Also, once formalized as a procedure,
+    a mathematical idea becomes a tool that can be used directly to
+    compute results.
+       - "Structure and Interpretation of Classical Mechanics",
+       Gerald Jay Sussman and Jack Wisdom with Meinhard E. Mayer
+
+.
+
+
+
+# Joy as glue language
+
+Basically any existing code/programs can be exposed to Joy as a function or collection of functions.
+
+## Shell command
+
+Run a shell command.
+
+        "stdin" "cmd line" system
+    -----------------------------------
+       "stderr" "stdout" return_code
+
+Then you can create e.g.:
+
+    foo == "awk {awk program}" system
+
+Etc...
+
+## Python libraries
+
+## Ctypes (FFI) for loading binary libraries
+
+
+
+
+# Git as File Store
+
+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.
+
+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.
+
+**Git is a functional data type**, compatible with the semantic model of Joy.  Implies shared datastore with obvious connection to git-archive & Datalad.
+
+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)
+
+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.)
+
+
+
+# Sympy Library
+
+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.
+
+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.
+
+
+
+# Stack-based laguages as Dataflow
+
+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.
+
+(Related to this I suspect that all stack chatter disappears during compilation but I haven't nailed that down yet.)
+
+I'm unable to find the original webpage that describe the above.  :-(
+
+
+# Appendix Partial Evaluation Example
+
+       k u F
+    -----------
+        u^k
+
+
+    k u 1 [popop 0 !=] [[popop odd][over *][]ifte [1 >>] dipd [sqr] dip] while [popop] dip
+
+    F == 1 [popop 0 !=] [[popop odd][over *][]ifte [1 >>] dipd [sqr] dip] while [popop] dip
+
+    5 u 1 [popop 0 !=] [[popop odd][over *][]ifte [1 >>] dipd [sqr] dip] while [popop] dip
+
+
+    5 u 1 popop 0 !=
+    5           0 !=
+    True
+
+
+    5 u 1 [popop odd][over *][]ifte [1 >>] dipd [sqr] dip
+    5 u 1 popop odd
+    True
+
+    w/ sqr == dup *
+
+    5 u 1 over * [1 >>] dipd [sqr] dip
+    5 u 1 u    * [1 >>] dipd [sqr] dip
+    5 u u        [1 >>] dipd [sqr] dip
+    5 1 >> u sqr u
+    2      u_dup_*   u
+          --or--
+    2      u_u_*   u
+
+    2 u_u_* u popop 0 !=
+    2               0 !=
+    True
+    
+    2 u_u_* u [popop odd][over *][]ifte [1 >>] dipd [sqr] dip
+    ...
+    2 u_u_* u                           [1 >>] dipd [sqr] dip
+
+    2 1 >> u_u_* sqr   u
+    1      u_u_*_dup_* u
+
+
+    1 u_u_*_dup_* u [popop odd][over *][]ifte [1 >>] dipd [sqr] dip
+    1 u_u_*_dup_* u             over *        [1 >>] dipd [sqr] dip
+    1 u_u_*_dup_* u u_u_*_dup_*      *        [1 >>] dipd [sqr] dip
+    1 u_u_*_dup_* u_u_u_*_dup_*_*             [1 >>] dipd [sqr] dip
+
+    1 1 >> u_u_*_dup_* sqr           u_u_u_*_dup_*_*
+    0      u_u_*_dup_* dup *         u_u_u_*_dup_*_*
+    0      u_u_*_dup_* u_u_*_dup_* * u_u_u_*_dup_*_*
+    0      u_..._*                   u_u_u_*_dup_*_*
+
+    0      u_..._*                   u_u_u_*_dup_*_* [popop] dip
+
+    u_u_u_*_dup_*_*
+    
+    ^5 == dup dup * dup * *
diff --git a/docs/README.md b/docs/README.md
new file mode 100644 (file)
index 0000000..5f1d4fa
--- /dev/null
@@ -0,0 +1,44 @@
+# Some Jupyter Notebooks and other material.
+
+All of the notebooks are also available as HTML and Markdown files (generated using nbconvert) so you can view them without running Jupyter.
+
+In order to run the [Jupyter Notebooks](https://jupyter.org/index.html) you need Jupyter (obviously) and you should install `Joypy`.  Here's an example using `virtualenv` from the `joypy/` directory:
+
+    virtualenv --system-site-packages <DIRNAME>
+    . ./<DIRNAME>/bin/activate
+    python ./setup.py install
+
+Once that's done you should be able to start Jupyter Notebook server with, e.g.:
+
+    python -n notebook
+
+This starts it using the `virtualenv` version of Python so `joy` will be available.  Navigate to the `joypy/docs` directory and the notebooks should be able to import the `notebook_preamble.py` file.
+
+## Table of Contents
+
+- 1. Basic Use of Joy in a Notebook
+- 2. Library Examples - Short examples of each word in the dictionary.  Various formats.
+- 3. Developing a Program - Working with the first problem from Project Euler, "Find the sum of all the multiples of 3 or 5 below 1000", several forms of the program are derived.
+- 4. Replacing Functions in the Dictionary - Shows the basics of defining new "primitive" functions in Python or as definitions and adding them to the dictionary.
+- Factorial Function and Paramorphisms - A basic pattern of recursive control-flow.
+- Generator Programs - Using the x combinator to make generator programs which can be used to create unbounded streams of values.
+- Hylo-, Ana-, Cata-morphisms - Some basic patterns of recursive control-flow structures.
+- Quadratic - Not-so-annoying Quadratic Formula.
+- Trees - Ordered Binary Trees in Joy and more recursion.
+- Zipper - A preliminary examination of the idea of data-structure "zippers" for traversing datastructures.
+- notebook_preamble.py - Imported into notebooks to simplify the preamble code.
+- pe1.py pe1.txt - Set up and execute a Joy program for the first problem from Project Euler. The pe1.txt file is the trace.  It's 2.8M uncompressed.  Compressed with gzip it becomes just 0.12M.
+- repl.py - Run this script to start a REPL.  Useful for e.g. running Joy code in a debugger.
+
+## Notes
+
+One of the things that interests me about Joy is how programming becomes
+less about writing code and more about sound reasoning about simple
+(almost geometric) programs.  Many of the notebooks in this collection
+consist of several pages of discussion to arrive at a few lines of Joy
+definitions.  I think this is a good thing.  This is "literate
+programming".  The "programs" resemble mathematical proofs.  You aren't
+implementing so much as deriving.  The structure of Joy seems to force
+you to think clearly about the task in a way that is reliable but
+extremely flexible.  It feels like a puzzle game, and the puzzles are
+often simple, and the solutions build on each other.