A bit more text in the Types notebook.
It's still pretty drafty. I'm working on the code and then I'll write up the last bit of docs.
This commit is contained in:
parent
f1979f35ce
commit
41d979b233
1282
docs/Types.html
1282
docs/Types.html
File diff suppressed because it is too large
Load Diff
1139
docs/Types.ipynb
1139
docs/Types.ipynb
File diff suppressed because it is too large
Load Diff
553
docs/Types.md
553
docs/Types.md
|
|
@ -1,7 +1,15 @@
|
|||
|
||||
# Type Inference
|
||||
|
||||
Cf. ["Type Inference in Stack-Based Programming Languages"](http://prl.ccs.neu.edu/blog/2017/03/10/type-inference-in-stack-based-programming-languages/) by Rob Kleffner, 2017-03-10.
|
||||
This notebook presents a simple type inferencer for Joy code. It can infer the stack effect of most Joy expressions. It built largely by means of existing ideas and research (some of it may be original but I'm not able to say, as I don't fully understand the all of the source material in the depth required to make that call.) A great overview of the existing knowledge is a talk ["Type Inference in Stack-Based Programming Languages"](http://prl.ccs.neu.edu/blog/2017/03/10/type-inference-in-stack-based-programming-languages/) given by Rob Kleffner on or about 2017-03-10 as part of a course on the history of programming languages.
|
||||
|
||||
The notebook starts with a simple inferencer based on the work of Jaanus Pöial which we then progressively elaborate to cover more Joy semantics. Along the way we write a simple "compiler" that emits Python code for what I like to call Yin functions.
|
||||
|
||||
Yin functions are those that only rearrange values in stacks, as opposed to Yang functions that actually work on the values themselves. It's interesting to note that a Joy with *only* stacks (no other kinds of values) can be made and is Turing-complete, therefore all Yang functions are actually Yin functions, and all computation can be performed by manipulations of structures of containers, which is a restatement of the Laws of Form. (Also, this implies that every program can be put into a form such that it can be computed in a single step, although that step may be enormous or unending.)
|
||||
|
||||
Although I haven't completed it yet, a Joy based on Laws of Form provides the foundation for a provably correct computing system "down to the metal". This is my original and still primary motivation for developing Joy. (I want a proven-correct Operating System for a swarm of trash-collecting recycler robots. To trust it I have to implementment it myself from first principles, and I'm not smart enough to truly grok the existing literature and software, so I had to go look for and find LoF and Joy. Now that I have the mental tools to build my robot OS I can get down to it.
|
||||
|
||||
Anyhow, here's type inference...
|
||||
|
||||
## Part I: Pöial's Rules
|
||||
|
||||
|
|
@ -608,7 +616,7 @@ C(cons, uncons)
|
|||
|
||||
|
||||
|
||||
## Part III: Compiling Stack Functions
|
||||
## Part III: Compiling Yin Functions
|
||||
Now consider the Python function we would like to derive:
|
||||
|
||||
|
||||
|
|
@ -1361,7 +1369,7 @@ neato(roll_up, swap, roll_down)
|
|||
|
||||
|
||||
```python
|
||||
# e.g. [popop] dip
|
||||
# e.g. [popop] dipd
|
||||
neato(popdd, roll_down, pop)
|
||||
```
|
||||
|
||||
|
|
@ -1421,6 +1429,8 @@ print compile_('sqr', C(dup, mul))
|
|||
return (n1, stack)
|
||||
|
||||
|
||||
(Eventually I should come back around to this becuase it's not tooo difficult to exend this code to be able to compile e.g. `n3 = mul(n1, n2)` for `mul` and insert it in the right place with the right variable names. It requires a little more support from the library functions, in that we need to know to call `mul()` the Python function for `mul` the Joy function, but since *most* of the math functions (at least) are already wrappers it should be straightforward.)
|
||||
|
||||
#### `compilable()`
|
||||
The functions that *can* be compiled are the ones that have only `AnyJoyType` and `StackJoyType` labels in their stack effect comments. We can write a function to check that:
|
||||
|
||||
|
|
@ -1895,7 +1905,7 @@ While in the second it spawns an `A`, which we will label `e`:
|
|||
w/ {d: e}
|
||||
[ A* b .0.] U [ .1.]
|
||||
w/ {.1.: A* b .0.}
|
||||
[ A* b .0.] U [ .1.]
|
||||
[ A* b .0.] U [ A* b .0.]
|
||||
|
||||
Giving us two unifiers:
|
||||
|
||||
|
|
@ -2235,15 +2245,13 @@ for result in unify(a, b):
|
|||
|
||||
## Part VII: Typing Combinators
|
||||
|
||||
TBD
|
||||
In order to compute the stack effect of combinators you kinda have to have the quoted programs they expect available. In the most general case, the `i` combinator, you can't say anything about it's stack effect other than it expects one quote:
|
||||
|
||||
This is an open subject.
|
||||
i (... [.1.] -- ... .1.)
|
||||
|
||||
The obvious thing is that you now need two pairs of tuples to describe the combinators' effects, a stack effect comment and an expression effect comment:
|
||||
Or
|
||||
|
||||
dip (a [F] --)--(-- F a)
|
||||
|
||||
One thing that might help is...
|
||||
i (... [A* .1.] -- ... A*)
|
||||
|
||||
Consider the type of:
|
||||
|
||||
|
|
@ -2258,210 +2266,9 @@ Obviously it would be:
|
|||
(a1 [..1] -- ... then what?
|
||||
|
||||
|
||||
Without any information about the contents of the quote we can't say much about the result.
|
||||
|
||||
```python
|
||||
class SymbolJoyType(AnyJoyType): prefix = 'F'
|
||||
|
||||
W = map(SymbolJoyType, _R)
|
||||
|
||||
k = S[0], ((W[1], S[2]), S[0])
|
||||
Symbol('cons')
|
||||
print doc_from_stack_effect(*k)
|
||||
|
||||
```
|
||||
|
||||
(-- [F1 .2.])
|
||||
|
||||
|
||||
|
||||
```python
|
||||
dip_a = ((W[1], S[2]), (A[1], S[0]))
|
||||
```
|
||||
|
||||
|
||||
```python
|
||||
d = relabel(S[0], dip_a)
|
||||
print doc_from_stack_effect(*d)
|
||||
```
|
||||
|
||||
(-- a1001 [F1001 .1002.])
|
||||
|
||||
|
||||
|
||||
```python
|
||||
s = list(unify(d[1], k[1]))[0]
|
||||
s
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
{s0: (a1001, s1000), s1002: s2, F1001: F1}
|
||||
|
||||
|
||||
|
||||
|
||||
```python
|
||||
j = update(s, k)
|
||||
```
|
||||
|
||||
|
||||
```python
|
||||
print doc_from_stack_effect(*j)
|
||||
```
|
||||
|
||||
(a1001 -- a1001 [F1 .2.])
|
||||
|
||||
|
||||
|
||||
```python
|
||||
j
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
((a1001, s1000), ((F1, s2), (a1001, s1000)))
|
||||
|
||||
|
||||
|
||||
|
||||
```python
|
||||
cons
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
((s1, (a1, s23)), ((a1, s1), s23))
|
||||
|
||||
|
||||
|
||||
|
||||
```python
|
||||
for f in MC([k], [dup]):
|
||||
print doc_from_stack_effect(*f)
|
||||
```
|
||||
|
||||
(-- [F0 .1.] [F0 .1.])
|
||||
|
||||
|
||||
|
||||
```python
|
||||
l = S[0], ((cons, S[2]), (A[1], S[0]))
|
||||
```
|
||||
|
||||
|
||||
```python
|
||||
print doc_from_stack_effect(*l)
|
||||
```
|
||||
|
||||
(-- a1 [[[[.1.] a1 .23.] [a1 .1.] .23.] .2.])
|
||||
|
||||
|
||||
|
||||
```python
|
||||
|
||||
def dip_t(F):
|
||||
(quote, (a1, sec)) = F[1]
|
||||
G = F[0], sec
|
||||
P = S[3], (a1, S[3])
|
||||
a = [P]
|
||||
while isinstance(quote, tuple):
|
||||
term, quote = quote
|
||||
a.append(term)
|
||||
a.append(G)
|
||||
return a[::-1]
|
||||
|
||||
|
||||
|
||||
|
||||
```
|
||||
|
||||
|
||||
```python
|
||||
from joy.utils.stack import iter_stack
|
||||
```
|
||||
|
||||
|
||||
```python
|
||||
a, b, c = dip_t(l)
|
||||
```
|
||||
|
||||
|
||||
```python
|
||||
a
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
(s0, s0)
|
||||
|
||||
|
||||
|
||||
|
||||
```python
|
||||
b
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
((s1, (a1, s23)), ((a1, s1), s23))
|
||||
|
||||
|
||||
|
||||
|
||||
```python
|
||||
c
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
(s3, (a1, s3))
|
||||
|
||||
|
||||
|
||||
|
||||
```python
|
||||
MC([a], [b])
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
[((s0, (a0, s1)), ((a0, s0), s1))]
|
||||
|
||||
|
||||
|
||||
|
||||
```python
|
||||
kjs = MC(MC([a], [b]), [c])
|
||||
kjs
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
[((s0, (a0, s1)), (a1, ((a0, s0), s1)))]
|
||||
|
||||
|
||||
|
||||
|
||||
```python
|
||||
print doc_from_stack_effect(*kjs[0])
|
||||
```
|
||||
|
||||
(a0 [.0.] -- [a0 .0.] a1)
|
||||
|
||||
|
||||
(a0 [.0.] -- [a0 .0.] a1)
|
||||
|
||||
a0 [.0.] a1 [cons] dip
|
||||
----------------------------
|
||||
[a0 .0.] a1
|
||||
I think there's a way forward. If we convert our list of terms we are composing into a stack structure we can use it as a *Joy expression*, then we can treat the *output half* of a function's stack effect comment as a Joy interpreter stack, and just execute combinators directly. We can hybridize the compostition function with an interpreter to evaluate combinators, compose non-combinator functions, and put type variables on the stack. For combinators like `branch` that can have more than one stack effect we have to "split universes" again and return both. (Note: bug! If one branch doesn't type check the currect code ignores it, so you can think things are okay but have a type error waiting in the faled branch, I think... D'oh! FIXME!!!)
|
||||
|
||||
|
||||
```python
|
||||
|
|
@ -2533,102 +2340,6 @@ And that brings us to current Work-In-Progress. I'm pretty hopeful that the mix
|
|||
|
||||
The rest of this stuff is junk and/or unfinished material.
|
||||
|
||||
### `concat`
|
||||
|
||||
How to deal with `concat`?
|
||||
|
||||
concat ([.0.] [.1.] -- [.0. .1.])
|
||||
|
||||
We would like to represent this in Python somehow...
|
||||
|
||||
|
||||
```python
|
||||
concat = (S[0], S[1]), ((S[0], S[1]),)
|
||||
```
|
||||
|
||||
But this is actually `cons` with the first argument restricted to be a stack:
|
||||
|
||||
([.0.] [.1.] -- [[.0.] .1.])
|
||||
|
||||
What we have implemented so far would actually only permit:
|
||||
|
||||
([.0.] [.1.] -- [.2.])
|
||||
|
||||
|
||||
```python
|
||||
concat = (S[0], S[1]), (S[2],)
|
||||
```
|
||||
|
||||
|
||||
Which works but can lose information. Consider `cons concat`, this is how much information we *could* retain:
|
||||
|
||||
(1 [.0.] [.1.] -- [1 .0. .1.])
|
||||
|
||||
As opposed to just:
|
||||
|
||||
(1 [.0.] [.1.] -- [.2.])
|
||||
|
||||
### represent `concat`
|
||||
|
||||
([.0.] [.1.] -- [A*(.0.) .1.])
|
||||
|
||||
Meaning that `A*` on the right-hand side should all the crap from `.0.`.
|
||||
|
||||
([ .0.] [.1.] -- [ A* .1.])
|
||||
([a .0.] [.1.] -- [a A* .1.])
|
||||
([a b .0.] [.1.] -- [a b A* .1.])
|
||||
([a b c .0.] [.1.] -- [a b c A* .1.])
|
||||
|
||||
|
||||
|
||||
or...
|
||||
|
||||
([ .0.] [.1.] -- [ .1.])
|
||||
([a .0.] [.1.] -- [a .1.])
|
||||
([a b .0.] [.1.] -- [a b .1.])
|
||||
([a b c .0.] [.1.] -- [a b c .1.])
|
||||
([a A* c .0.] [.1.] -- [a A* c .1.])
|
||||
|
||||
|
||||
|
||||
(a, (b, S0)) . S1 = (a, (b, (A*, S1)))
|
||||
|
||||
|
||||
```python
|
||||
class Astar(object):
|
||||
def __repr__(self):
|
||||
return 'A*'
|
||||
|
||||
|
||||
def concat(s0, s1):
|
||||
a = []
|
||||
while isinstance(s0, tuple):
|
||||
term, s0 = s0
|
||||
a.append(term)
|
||||
assert isinstance(s0, StackJoyType), repr(s0)
|
||||
s1 = Astar(), s1
|
||||
for term in reversed(a):
|
||||
s1 = term, s1
|
||||
return s1
|
||||
```
|
||||
|
||||
|
||||
```python
|
||||
a, b = (A[1], S[0]), (A[2], S[1])
|
||||
```
|
||||
|
||||
|
||||
```python
|
||||
concat(a, b)
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
(a1, (A*, (a2, s1)))
|
||||
|
||||
|
||||
|
||||
## Appendix: Joy in the Logical Paradigm
|
||||
For this to work the type label classes have to be modified to let `T >= t` succeed, where e.g. `T` is `IntJoyType` and `t` is `int`
|
||||
|
||||
|
|
@ -2644,7 +2355,7 @@ print doc_from_stack_effect(*F)
|
|||
|
||||
ValueError Traceback (most recent call last)
|
||||
|
||||
<ipython-input-137-4b4cb6ff86e5> in <module>()
|
||||
<ipython-input-112-4b4cb6ff86e5> in <module>()
|
||||
1 F = reduce(C, (pop, swap, roll_down, rest, rest, cons, cons))
|
||||
2
|
||||
----> 3 print doc_from_stack_effect(*F)
|
||||
|
|
@ -2764,3 +2475,225 @@ s[0]
|
|||
I *think* this might be sorta what I'm doing above with the `kav()` function...
|
||||
In any event "mixed-mode" interpreters that include values and type variables and can track constraints, etc. will be, uh, super-useful. And Abstract Interpretation should be a rich source of ideas.
|
||||
|
||||
|
||||
## Junk
|
||||
|
||||
|
||||
```python
|
||||
class SymbolJoyType(AnyJoyType): prefix = 'F'
|
||||
|
||||
W = map(SymbolJoyType, _R)
|
||||
|
||||
k = S[0], ((W[1], S[2]), S[0])
|
||||
Symbol('cons')
|
||||
print doc_from_stack_effect(*k)
|
||||
|
||||
```
|
||||
|
||||
|
||||
```python
|
||||
dip_a = ((W[1], S[2]), (A[1], S[0]))
|
||||
```
|
||||
|
||||
|
||||
```python
|
||||
d = relabel(S[0], dip_a)
|
||||
print doc_from_stack_effect(*d)
|
||||
```
|
||||
|
||||
|
||||
```python
|
||||
s = list(unify(d[1], k[1]))[0]
|
||||
s
|
||||
```
|
||||
|
||||
|
||||
```python
|
||||
j = update(s, k)
|
||||
```
|
||||
|
||||
|
||||
```python
|
||||
print doc_from_stack_effect(*j)
|
||||
```
|
||||
|
||||
|
||||
```python
|
||||
j
|
||||
```
|
||||
|
||||
|
||||
```python
|
||||
cons
|
||||
```
|
||||
|
||||
|
||||
```python
|
||||
for f in MC([k], [dup]):
|
||||
print doc_from_stack_effect(*f)
|
||||
```
|
||||
|
||||
|
||||
```python
|
||||
l = S[0], ((cons, S[2]), (A[1], S[0]))
|
||||
```
|
||||
|
||||
|
||||
```python
|
||||
print doc_from_stack_effect(*l)
|
||||
```
|
||||
|
||||
|
||||
```python
|
||||
|
||||
def dip_t(F):
|
||||
(quote, (a1, sec)) = F[1]
|
||||
G = F[0], sec
|
||||
P = S[3], (a1, S[3])
|
||||
a = [P]
|
||||
while isinstance(quote, tuple):
|
||||
term, quote = quote
|
||||
a.append(term)
|
||||
a.append(G)
|
||||
return a[::-1]
|
||||
|
||||
|
||||
|
||||
|
||||
```
|
||||
|
||||
|
||||
```python
|
||||
from joy.utils.stack import iter_stack
|
||||
```
|
||||
|
||||
|
||||
```python
|
||||
a, b, c = dip_t(l)
|
||||
```
|
||||
|
||||
|
||||
```python
|
||||
a
|
||||
```
|
||||
|
||||
|
||||
```python
|
||||
b
|
||||
```
|
||||
|
||||
|
||||
```python
|
||||
c
|
||||
```
|
||||
|
||||
|
||||
```python
|
||||
MC([a], [b])
|
||||
```
|
||||
|
||||
|
||||
```python
|
||||
kjs = MC(MC([a], [b]), [c])
|
||||
kjs
|
||||
```
|
||||
|
||||
|
||||
```python
|
||||
print doc_from_stack_effect(*kjs[0])
|
||||
```
|
||||
|
||||
(a0 [.0.] -- [a0 .0.] a1)
|
||||
|
||||
a0 [.0.] a1 [cons] dip
|
||||
----------------------------
|
||||
[a0 .0.] a1
|
||||
|
||||
### `concat`
|
||||
|
||||
How to deal with `concat`?
|
||||
|
||||
concat ([.0.] [.1.] -- [.0. .1.])
|
||||
|
||||
We would like to represent this in Python somehow...
|
||||
|
||||
|
||||
```python
|
||||
concat = (S[0], S[1]), ((S[0], S[1]),)
|
||||
```
|
||||
|
||||
But this is actually `cons` with the first argument restricted to be a stack:
|
||||
|
||||
([.0.] [.1.] -- [[.0.] .1.])
|
||||
|
||||
What we have implemented so far would actually only permit:
|
||||
|
||||
([.0.] [.1.] -- [.2.])
|
||||
|
||||
|
||||
```python
|
||||
concat = (S[0], S[1]), (S[2],)
|
||||
```
|
||||
|
||||
|
||||
Which works but can lose information. Consider `cons concat`, this is how much information we *could* retain:
|
||||
|
||||
(1 [.0.] [.1.] -- [1 .0. .1.])
|
||||
|
||||
As opposed to just:
|
||||
|
||||
(1 [.0.] [.1.] -- [.2.])
|
||||
|
||||
### represent `concat`
|
||||
|
||||
([.0.] [.1.] -- [A*(.0.) .1.])
|
||||
|
||||
Meaning that `A*` on the right-hand side should all the crap from `.0.`.
|
||||
|
||||
([ .0.] [.1.] -- [ A* .1.])
|
||||
([a .0.] [.1.] -- [a A* .1.])
|
||||
([a b .0.] [.1.] -- [a b A* .1.])
|
||||
([a b c .0.] [.1.] -- [a b c A* .1.])
|
||||
|
||||
|
||||
|
||||
or...
|
||||
|
||||
([ .0.] [.1.] -- [ .1.])
|
||||
([a .0.] [.1.] -- [a .1.])
|
||||
([a b .0.] [.1.] -- [a b .1.])
|
||||
([a b c .0.] [.1.] -- [a b c .1.])
|
||||
([a A* c .0.] [.1.] -- [a A* c .1.])
|
||||
|
||||
|
||||
|
||||
(a, (b, S0)) . S1 = (a, (b, (A*, S1)))
|
||||
|
||||
|
||||
```python
|
||||
class Astar(object):
|
||||
def __repr__(self):
|
||||
return 'A*'
|
||||
|
||||
|
||||
def concat(s0, s1):
|
||||
a = []
|
||||
while isinstance(s0, tuple):
|
||||
term, s0 = s0
|
||||
a.append(term)
|
||||
assert isinstance(s0, StackJoyType), repr(s0)
|
||||
s1 = Astar(), s1
|
||||
for term in reversed(a):
|
||||
s1 = term, s1
|
||||
return s1
|
||||
```
|
||||
|
||||
|
||||
```python
|
||||
a, b = (A[1], S[0]), (A[2], S[1])
|
||||
```
|
||||
|
||||
|
||||
```python
|
||||
concat(a, b)
|
||||
```
|
||||
|
|
|
|||
638
docs/Types.rst
638
docs/Types.rst
|
|
@ -2,9 +2,43 @@
|
|||
Type Inference
|
||||
==============
|
||||
|
||||
Cf. `"Type Inference in Stack-Based Programming
|
||||
This notebook presents a simple type inferencer for Joy code. It can
|
||||
infer the stack effect of most Joy expressions. It built largely by
|
||||
means of existing ideas and research (some of it may be original but I'm
|
||||
not able to say, as I don't fully understand the all of the source
|
||||
material in the depth required to make that call.) A great overview of
|
||||
the existing knowledge is a talk `"Type Inference in Stack-Based
|
||||
Programming
|
||||
Languages" <http://prl.ccs.neu.edu/blog/2017/03/10/type-inference-in-stack-based-programming-languages/>`__
|
||||
by Rob Kleffner, 2017-03-10.
|
||||
given by Rob Kleffner on or about 2017-03-10 as part of a course on the
|
||||
history of programming languages.
|
||||
|
||||
The notebook starts with a simple inferencer based on the work of Jaanus
|
||||
Pöial which we then progressively elaborate to cover more Joy semantics.
|
||||
Along the way we write a simple "compiler" that emits Python code for
|
||||
what I like to call Yin functions.
|
||||
|
||||
Yin functions are those that only rearrange values in stacks, as opposed
|
||||
to Yang functions that actually work on the values themselves. It's
|
||||
interesting to note that a Joy with *only* stacks (no other kinds of
|
||||
values) can be made and is Turing-complete, therefore all Yang functions
|
||||
are actually Yin functions, and all computation can be performed by
|
||||
manipulations of structures of containers, which is a restatement of the
|
||||
Laws of Form. (Also, this implies that every program can be put into a
|
||||
form such that it can be computed in a single step, although that step
|
||||
may be enormous or unending.)
|
||||
|
||||
Although I haven't completed it yet, a Joy based on Laws of Form
|
||||
provides the foundation for a provably correct computing system "down to
|
||||
the metal". This is my original and still primary motivation for
|
||||
developing Joy. (I want a proven-correct Operating System for a swarm of
|
||||
trash-collecting recycler robots. To trust it I have to implementment it
|
||||
myself from first principles, and I'm not smart enough to truly grok the
|
||||
existing literature and software, so I had to go look for and find LoF
|
||||
and Joy. Now that I have the mental tools to build my robot OS I can get
|
||||
down to it.
|
||||
|
||||
Anyhow, here's type inference...
|
||||
|
||||
Part I: Pöial's Rules
|
||||
---------------------
|
||||
|
|
@ -761,8 +795,8 @@ deal with this recursively:
|
|||
|
||||
|
||||
|
||||
Part III: Compiling Stack Functions
|
||||
-----------------------------------
|
||||
Part III: Compiling Yin Functions
|
||||
---------------------------------
|
||||
|
||||
Now consider the Python function we would like to derive:
|
||||
|
||||
|
|
@ -1605,7 +1639,7 @@ also get the effect of combinators in some limited cases.
|
|||
|
||||
.. code:: ipython2
|
||||
|
||||
# e.g. [popop] dip
|
||||
# e.g. [popop] dipd
|
||||
neato(popdd, roll_down, pop)
|
||||
|
||||
|
||||
|
|
@ -1676,6 +1710,14 @@ such. Note that this is *not* a ``sqr`` function implementation:
|
|||
return (n1, stack)
|
||||
|
||||
|
||||
(Eventually I should come back around to this becuase it's not tooo
|
||||
difficult to exend this code to be able to compile e.g.
|
||||
``n3 = mul(n1, n2)`` for ``mul`` and insert it in the right place with
|
||||
the right variable names. It requires a little more support from the
|
||||
library functions, in that we need to know to call ``mul()`` the Python
|
||||
function for ``mul`` the Joy function, but since *most* of the math
|
||||
functions (at least) are already wrappers it should be straightforward.)
|
||||
|
||||
``compilable()``
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
|
|
@ -2231,7 +2273,7 @@ While in the second it spawns an ``A``, which we will label ``e``:
|
|||
w/ {d: e}
|
||||
[ A* b .0.] U [ .1.]
|
||||
w/ {.1.: A* b .0.}
|
||||
[ A* b .0.] U [ .1.]
|
||||
[ A* b .0.] U [ A* b .0.]
|
||||
|
||||
Giving us two unifiers:
|
||||
|
||||
|
|
@ -2601,19 +2643,20 @@ This function has to be modified to yield multiple results.
|
|||
Part VII: Typing Combinators
|
||||
----------------------------
|
||||
|
||||
TBD
|
||||
|
||||
This is an open subject.
|
||||
|
||||
The obvious thing is that you now need two pairs of tuples to describe
|
||||
the combinators' effects, a stack effect comment and an expression
|
||||
effect comment:
|
||||
In order to compute the stack effect of combinators you kinda have to
|
||||
have the quoted programs they expect available. In the most general
|
||||
case, the ``i`` combinator, you can't say anything about it's stack
|
||||
effect other than it expects one quote:
|
||||
|
||||
::
|
||||
|
||||
dip (a [F] --)--(-- F a)
|
||||
i (... [.1.] -- ... .1.)
|
||||
|
||||
One thing that might help is...
|
||||
Or
|
||||
|
||||
::
|
||||
|
||||
i (... [A* .1.] -- ... A*)
|
||||
|
||||
Consider the type of:
|
||||
|
||||
|
|
@ -2633,226 +2676,20 @@ Obviously it would be:
|
|||
|
||||
(a1 [..1] -- ... then what?
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
class SymbolJoyType(AnyJoyType): prefix = 'F'
|
||||
|
||||
W = map(SymbolJoyType, _R)
|
||||
|
||||
k = S[0], ((W[1], S[2]), S[0])
|
||||
Symbol('cons')
|
||||
print doc_from_stack_effect(*k)
|
||||
|
||||
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
(-- [F1 .2.])
|
||||
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
dip_a = ((W[1], S[2]), (A[1], S[0]))
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
d = relabel(S[0], dip_a)
|
||||
print doc_from_stack_effect(*d)
|
||||
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
(-- a1001 [F1001 .1002.])
|
||||
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
s = list(unify(d[1], k[1]))[0]
|
||||
s
|
||||
|
||||
|
||||
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
{s0: (a1001, s1000), s1002: s2, F1001: F1}
|
||||
|
||||
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
j = update(s, k)
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
print doc_from_stack_effect(*j)
|
||||
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
(a1001 -- a1001 [F1 .2.])
|
||||
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
j
|
||||
|
||||
|
||||
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
((a1001, s1000), ((F1, s2), (a1001, s1000)))
|
||||
|
||||
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
cons
|
||||
|
||||
|
||||
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
((s1, (a1, s23)), ((a1, s1), s23))
|
||||
|
||||
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
for f in MC([k], [dup]):
|
||||
print doc_from_stack_effect(*f)
|
||||
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
(-- [F0 .1.] [F0 .1.])
|
||||
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
l = S[0], ((cons, S[2]), (A[1], S[0]))
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
print doc_from_stack_effect(*l)
|
||||
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
(-- a1 [[[[.1.] a1 .23.] [a1 .1.] .23.] .2.])
|
||||
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
|
||||
def dip_t(F):
|
||||
(quote, (a1, sec)) = F[1]
|
||||
G = F[0], sec
|
||||
P = S[3], (a1, S[3])
|
||||
a = [P]
|
||||
while isinstance(quote, tuple):
|
||||
term, quote = quote
|
||||
a.append(term)
|
||||
a.append(G)
|
||||
return a[::-1]
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
from joy.utils.stack import iter_stack
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
a, b, c = dip_t(l)
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
a
|
||||
|
||||
|
||||
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
(s0, s0)
|
||||
|
||||
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
b
|
||||
|
||||
|
||||
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
((s1, (a1, s23)), ((a1, s1), s23))
|
||||
|
||||
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
c
|
||||
|
||||
|
||||
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
(s3, (a1, s3))
|
||||
|
||||
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
MC([a], [b])
|
||||
|
||||
|
||||
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
[((s0, (a0, s1)), ((a0, s0), s1))]
|
||||
|
||||
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
kjs = MC(MC([a], [b]), [c])
|
||||
kjs
|
||||
|
||||
|
||||
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
[((s0, (a0, s1)), (a1, ((a0, s0), s1)))]
|
||||
|
||||
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
print doc_from_stack_effect(*kjs[0])
|
||||
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
(a0 [.0.] -- [a0 .0.] a1)
|
||||
|
||||
|
||||
::
|
||||
|
||||
(a0 [.0.] -- [a0 .0.] a1)
|
||||
|
||||
a0 [.0.] a1 [cons] dip
|
||||
----------------------------
|
||||
[a0 .0.] a1
|
||||
Without any information about the contents of the quote we can't say
|
||||
much about the result.
|
||||
|
||||
I think there's a way forward. If we convert our list of terms we are
|
||||
composing into a stack structure we can use it as a *Joy expression*,
|
||||
then we can treat the *output half* of a function's stack effect comment
|
||||
as a Joy interpreter stack, and just execute combinators directly. We
|
||||
can hybridize the compostition function with an interpreter to evaluate
|
||||
combinators, compose non-combinator functions, and put type variables on
|
||||
the stack. For combinators like ``branch`` that can have more than one
|
||||
stack effect we have to "split universes" again and return both. (Note:
|
||||
bug! If one branch doesn't type check the currect code ignores it, so
|
||||
you can think things are okay but have a type error waiting in the faled
|
||||
branch, I think... D'oh! FIXME!!!)
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
|
|
@ -2924,117 +2761,6 @@ the pace I should be able to verify that conjecture by the end of June.
|
|||
|
||||
The rest of this stuff is junk and/or unfinished material.
|
||||
|
||||
``concat``
|
||||
~~~~~~~~~~
|
||||
|
||||
How to deal with ``concat``?
|
||||
|
||||
::
|
||||
|
||||
concat ([.0.] [.1.] -- [.0. .1.])
|
||||
|
||||
We would like to represent this in Python somehow...
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
concat = (S[0], S[1]), ((S[0], S[1]),)
|
||||
|
||||
But this is actually ``cons`` with the first argument restricted to be a
|
||||
stack:
|
||||
|
||||
::
|
||||
|
||||
([.0.] [.1.] -- [[.0.] .1.])
|
||||
|
||||
What we have implemented so far would actually only permit:
|
||||
|
||||
::
|
||||
|
||||
([.0.] [.1.] -- [.2.])
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
concat = (S[0], S[1]), (S[2],)
|
||||
|
||||
Which works but can lose information. Consider ``cons concat``, this is
|
||||
how much information we *could* retain:
|
||||
|
||||
::
|
||||
|
||||
(1 [.0.] [.1.] -- [1 .0. .1.])
|
||||
|
||||
As opposed to just:
|
||||
|
||||
::
|
||||
|
||||
(1 [.0.] [.1.] -- [.2.])
|
||||
|
||||
represent ``concat``
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
::
|
||||
|
||||
([.0.] [.1.] -- [A*(.0.) .1.])
|
||||
|
||||
Meaning that ``A*`` on the right-hand side should all the crap from
|
||||
``.0.``.
|
||||
|
||||
::
|
||||
|
||||
([ .0.] [.1.] -- [ A* .1.])
|
||||
([a .0.] [.1.] -- [a A* .1.])
|
||||
([a b .0.] [.1.] -- [a b A* .1.])
|
||||
([a b c .0.] [.1.] -- [a b c A* .1.])
|
||||
|
||||
or...
|
||||
|
||||
::
|
||||
|
||||
([ .0.] [.1.] -- [ .1.])
|
||||
([a .0.] [.1.] -- [a .1.])
|
||||
([a b .0.] [.1.] -- [a b .1.])
|
||||
([a b c .0.] [.1.] -- [a b c .1.])
|
||||
([a A* c .0.] [.1.] -- [a A* c .1.])
|
||||
|
||||
::
|
||||
|
||||
(a, (b, S0)) . S1 = (a, (b, (A*, S1)))
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
class Astar(object):
|
||||
def __repr__(self):
|
||||
return 'A*'
|
||||
|
||||
|
||||
def concat(s0, s1):
|
||||
a = []
|
||||
while isinstance(s0, tuple):
|
||||
term, s0 = s0
|
||||
a.append(term)
|
||||
assert isinstance(s0, StackJoyType), repr(s0)
|
||||
s1 = Astar(), s1
|
||||
for term in reversed(a):
|
||||
s1 = term, s1
|
||||
return s1
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
a, b = (A[1], S[0]), (A[2], S[1])
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
concat(a, b)
|
||||
|
||||
|
||||
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
(a1, (A*, (a2, s1)))
|
||||
|
||||
|
||||
|
||||
Appendix: Joy in the Logical Paradigm
|
||||
-------------------------------------
|
||||
|
||||
|
|
@ -3056,7 +2782,7 @@ For this to work the type label classes have to be modified to let
|
|||
|
||||
ValueError Traceback (most recent call last)
|
||||
|
||||
<ipython-input-137-4b4cb6ff86e5> in <module>()
|
||||
<ipython-input-112-4b4cb6ff86e5> in <module>()
|
||||
1 F = reduce(C, (pop, swap, roll_down, rest, rest, cons, cons))
|
||||
2
|
||||
----> 3 print doc_from_stack_effect(*F)
|
||||
|
|
@ -3174,3 +2900,221 @@ function... In any event "mixed-mode" interpreters that include values
|
|||
and type variables and can track constraints, etc. will be, uh,
|
||||
super-useful. And Abstract Interpretation should be a rich source of
|
||||
ideas.
|
||||
|
||||
Junk
|
||||
----
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
class SymbolJoyType(AnyJoyType): prefix = 'F'
|
||||
|
||||
W = map(SymbolJoyType, _R)
|
||||
|
||||
k = S[0], ((W[1], S[2]), S[0])
|
||||
Symbol('cons')
|
||||
print doc_from_stack_effect(*k)
|
||||
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
dip_a = ((W[1], S[2]), (A[1], S[0]))
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
d = relabel(S[0], dip_a)
|
||||
print doc_from_stack_effect(*d)
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
s = list(unify(d[1], k[1]))[0]
|
||||
s
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
j = update(s, k)
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
print doc_from_stack_effect(*j)
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
j
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
cons
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
for f in MC([k], [dup]):
|
||||
print doc_from_stack_effect(*f)
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
l = S[0], ((cons, S[2]), (A[1], S[0]))
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
print doc_from_stack_effect(*l)
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
|
||||
def dip_t(F):
|
||||
(quote, (a1, sec)) = F[1]
|
||||
G = F[0], sec
|
||||
P = S[3], (a1, S[3])
|
||||
a = [P]
|
||||
while isinstance(quote, tuple):
|
||||
term, quote = quote
|
||||
a.append(term)
|
||||
a.append(G)
|
||||
return a[::-1]
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
from joy.utils.stack import iter_stack
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
a, b, c = dip_t(l)
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
a
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
b
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
c
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
MC([a], [b])
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
kjs = MC(MC([a], [b]), [c])
|
||||
kjs
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
print doc_from_stack_effect(*kjs[0])
|
||||
|
||||
::
|
||||
|
||||
(a0 [.0.] -- [a0 .0.] a1)
|
||||
|
||||
a0 [.0.] a1 [cons] dip
|
||||
----------------------------
|
||||
[a0 .0.] a1
|
||||
|
||||
``concat``
|
||||
~~~~~~~~~~
|
||||
|
||||
How to deal with ``concat``?
|
||||
|
||||
::
|
||||
|
||||
concat ([.0.] [.1.] -- [.0. .1.])
|
||||
|
||||
We would like to represent this in Python somehow...
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
concat = (S[0], S[1]), ((S[0], S[1]),)
|
||||
|
||||
But this is actually ``cons`` with the first argument restricted to be a
|
||||
stack:
|
||||
|
||||
::
|
||||
|
||||
([.0.] [.1.] -- [[.0.] .1.])
|
||||
|
||||
What we have implemented so far would actually only permit:
|
||||
|
||||
::
|
||||
|
||||
([.0.] [.1.] -- [.2.])
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
concat = (S[0], S[1]), (S[2],)
|
||||
|
||||
Which works but can lose information. Consider ``cons concat``, this is
|
||||
how much information we *could* retain:
|
||||
|
||||
::
|
||||
|
||||
(1 [.0.] [.1.] -- [1 .0. .1.])
|
||||
|
||||
As opposed to just:
|
||||
|
||||
::
|
||||
|
||||
(1 [.0.] [.1.] -- [.2.])
|
||||
|
||||
represent ``concat``
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
::
|
||||
|
||||
([.0.] [.1.] -- [A*(.0.) .1.])
|
||||
|
||||
Meaning that ``A*`` on the right-hand side should all the crap from
|
||||
``.0.``.
|
||||
|
||||
::
|
||||
|
||||
([ .0.] [.1.] -- [ A* .1.])
|
||||
([a .0.] [.1.] -- [a A* .1.])
|
||||
([a b .0.] [.1.] -- [a b A* .1.])
|
||||
([a b c .0.] [.1.] -- [a b c A* .1.])
|
||||
|
||||
or...
|
||||
|
||||
::
|
||||
|
||||
([ .0.] [.1.] -- [ .1.])
|
||||
([a .0.] [.1.] -- [a .1.])
|
||||
([a b .0.] [.1.] -- [a b .1.])
|
||||
([a b c .0.] [.1.] -- [a b c .1.])
|
||||
([a A* c .0.] [.1.] -- [a A* c .1.])
|
||||
|
||||
::
|
||||
|
||||
(a, (b, S0)) . S1 = (a, (b, (A*, S1)))
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
class Astar(object):
|
||||
def __repr__(self):
|
||||
return 'A*'
|
||||
|
||||
|
||||
def concat(s0, s1):
|
||||
a = []
|
||||
while isinstance(s0, tuple):
|
||||
term, s0 = s0
|
||||
a.append(term)
|
||||
assert isinstance(s0, StackJoyType), repr(s0)
|
||||
s1 = Astar(), s1
|
||||
for term in reversed(a):
|
||||
s1 = term, s1
|
||||
return s1
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
a, b = (A[1], S[0]), (A[2], S[1])
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
concat(a, b)
|
||||
|
|
|
|||
|
|
@ -121,13 +121,14 @@
|
|||
<li class="toctree-l1"><a class="reference internal" href="Types.html">Type Inference</a><ul>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Types.html#part-i-poial-s-rules">Part I: Pöial’s Rules</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Types.html#part-ii-implementation">Part II: Implementation</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Types.html#part-iii-compiling-stack-functions">Part III: Compiling Stack Functions</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Types.html#part-iii-compiling-yin-functions">Part III: Compiling Yin Functions</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Types.html#part-iv-types-and-subtypes-of-arguments">Part IV: Types and Subtypes of Arguments</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Types.html#part-v-functions-that-use-the-stack">Part V: Functions that use the Stack</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Types.html#part-vi-multiple-stack-effects">Part VI: Multiple Stack Effects</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Types.html#part-vii-typing-combinators">Part VII: Typing Combinators</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Types.html#appendix-joy-in-the-logical-paradigm">Appendix: Joy in the Logical Paradigm</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Types.html#abstract-interpretation">Abstract Interpretation</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="Types.html#junk">Junk</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="NoUpdates.html">No Updates</a></li>
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -2,9 +2,43 @@
|
|||
Type Inference
|
||||
==============
|
||||
|
||||
Cf. `"Type Inference in Stack-Based Programming
|
||||
This notebook presents a simple type inferencer for Joy code. It can
|
||||
infer the stack effect of most Joy expressions. It built largely by
|
||||
means of existing ideas and research (some of it may be original but I'm
|
||||
not able to say, as I don't fully understand the all of the source
|
||||
material in the depth required to make that call.) A great overview of
|
||||
the existing knowledge is a talk `"Type Inference in Stack-Based
|
||||
Programming
|
||||
Languages" <http://prl.ccs.neu.edu/blog/2017/03/10/type-inference-in-stack-based-programming-languages/>`__
|
||||
by Rob Kleffner, 2017-03-10.
|
||||
given by Rob Kleffner on or about 2017-03-10 as part of a course on the
|
||||
history of programming languages.
|
||||
|
||||
The notebook starts with a simple inferencer based on the work of Jaanus
|
||||
Pöial which we then progressively elaborate to cover more Joy semantics.
|
||||
Along the way we write a simple "compiler" that emits Python code for
|
||||
what I like to call Yin functions.
|
||||
|
||||
Yin functions are those that only rearrange values in stacks, as opposed
|
||||
to Yang functions that actually work on the values themselves. It's
|
||||
interesting to note that a Joy with *only* stacks (no other kinds of
|
||||
values) can be made and is Turing-complete, therefore all Yang functions
|
||||
are actually Yin functions, and all computation can be performed by
|
||||
manipulations of structures of containers, which is a restatement of the
|
||||
Laws of Form. (Also, this implies that every program can be put into a
|
||||
form such that it can be computed in a single step, although that step
|
||||
may be enormous or unending.)
|
||||
|
||||
Although I haven't completed it yet, a Joy based on Laws of Form
|
||||
provides the foundation for a provably correct computing system "down to
|
||||
the metal". This is my original and still primary motivation for
|
||||
developing Joy. (I want a proven-correct Operating System for a swarm of
|
||||
trash-collecting recycler robots. To trust it I have to implementment it
|
||||
myself from first principles, and I'm not smart enough to truly grok the
|
||||
existing literature and software, so I had to go look for and find LoF
|
||||
and Joy. Now that I have the mental tools to build my robot OS I can get
|
||||
down to it.
|
||||
|
||||
Anyhow, here's type inference...
|
||||
|
||||
Part I: Pöial's Rules
|
||||
---------------------
|
||||
|
|
@ -761,8 +795,8 @@ deal with this recursively:
|
|||
|
||||
|
||||
|
||||
Part III: Compiling Stack Functions
|
||||
-----------------------------------
|
||||
Part III: Compiling Yin Functions
|
||||
---------------------------------
|
||||
|
||||
Now consider the Python function we would like to derive:
|
||||
|
||||
|
|
@ -1605,7 +1639,7 @@ also get the effect of combinators in some limited cases.
|
|||
|
||||
.. code:: ipython2
|
||||
|
||||
# e.g. [popop] dip
|
||||
# e.g. [popop] dipd
|
||||
neato(popdd, roll_down, pop)
|
||||
|
||||
|
||||
|
|
@ -1676,6 +1710,14 @@ such. Note that this is *not* a ``sqr`` function implementation:
|
|||
return (n1, stack)
|
||||
|
||||
|
||||
(Eventually I should come back around to this becuase it's not tooo
|
||||
difficult to exend this code to be able to compile e.g.
|
||||
``n3 = mul(n1, n2)`` for ``mul`` and insert it in the right place with
|
||||
the right variable names. It requires a little more support from the
|
||||
library functions, in that we need to know to call ``mul()`` the Python
|
||||
function for ``mul`` the Joy function, but since *most* of the math
|
||||
functions (at least) are already wrappers it should be straightforward.)
|
||||
|
||||
``compilable()``
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
|
|
@ -2231,7 +2273,7 @@ While in the second it spawns an ``A``, which we will label ``e``:
|
|||
w/ {d: e}
|
||||
[ A* b .0.] U [ .1.]
|
||||
w/ {.1.: A* b .0.}
|
||||
[ A* b .0.] U [ .1.]
|
||||
[ A* b .0.] U [ A* b .0.]
|
||||
|
||||
Giving us two unifiers:
|
||||
|
||||
|
|
@ -2601,19 +2643,20 @@ This function has to be modified to yield multiple results.
|
|||
Part VII: Typing Combinators
|
||||
----------------------------
|
||||
|
||||
TBD
|
||||
|
||||
This is an open subject.
|
||||
|
||||
The obvious thing is that you now need two pairs of tuples to describe
|
||||
the combinators' effects, a stack effect comment and an expression
|
||||
effect comment:
|
||||
In order to compute the stack effect of combinators you kinda have to
|
||||
have the quoted programs they expect available. In the most general
|
||||
case, the ``i`` combinator, you can't say anything about it's stack
|
||||
effect other than it expects one quote:
|
||||
|
||||
::
|
||||
|
||||
dip (a [F] --)--(-- F a)
|
||||
i (... [.1.] -- ... .1.)
|
||||
|
||||
One thing that might help is...
|
||||
Or
|
||||
|
||||
::
|
||||
|
||||
i (... [A* .1.] -- ... A*)
|
||||
|
||||
Consider the type of:
|
||||
|
||||
|
|
@ -2633,226 +2676,20 @@ Obviously it would be:
|
|||
|
||||
(a1 [..1] -- ... then what?
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
class SymbolJoyType(AnyJoyType): prefix = 'F'
|
||||
|
||||
W = map(SymbolJoyType, _R)
|
||||
|
||||
k = S[0], ((W[1], S[2]), S[0])
|
||||
Symbol('cons')
|
||||
print doc_from_stack_effect(*k)
|
||||
|
||||
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
(-- [F1 .2.])
|
||||
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
dip_a = ((W[1], S[2]), (A[1], S[0]))
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
d = relabel(S[0], dip_a)
|
||||
print doc_from_stack_effect(*d)
|
||||
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
(-- a1001 [F1001 .1002.])
|
||||
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
s = list(unify(d[1], k[1]))[0]
|
||||
s
|
||||
|
||||
|
||||
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
{s0: (a1001, s1000), s1002: s2, F1001: F1}
|
||||
|
||||
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
j = update(s, k)
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
print doc_from_stack_effect(*j)
|
||||
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
(a1001 -- a1001 [F1 .2.])
|
||||
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
j
|
||||
|
||||
|
||||
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
((a1001, s1000), ((F1, s2), (a1001, s1000)))
|
||||
|
||||
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
cons
|
||||
|
||||
|
||||
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
((s1, (a1, s23)), ((a1, s1), s23))
|
||||
|
||||
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
for f in MC([k], [dup]):
|
||||
print doc_from_stack_effect(*f)
|
||||
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
(-- [F0 .1.] [F0 .1.])
|
||||
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
l = S[0], ((cons, S[2]), (A[1], S[0]))
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
print doc_from_stack_effect(*l)
|
||||
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
(-- a1 [[[[.1.] a1 .23.] [a1 .1.] .23.] .2.])
|
||||
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
|
||||
def dip_t(F):
|
||||
(quote, (a1, sec)) = F[1]
|
||||
G = F[0], sec
|
||||
P = S[3], (a1, S[3])
|
||||
a = [P]
|
||||
while isinstance(quote, tuple):
|
||||
term, quote = quote
|
||||
a.append(term)
|
||||
a.append(G)
|
||||
return a[::-1]
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
from joy.utils.stack import iter_stack
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
a, b, c = dip_t(l)
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
a
|
||||
|
||||
|
||||
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
(s0, s0)
|
||||
|
||||
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
b
|
||||
|
||||
|
||||
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
((s1, (a1, s23)), ((a1, s1), s23))
|
||||
|
||||
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
c
|
||||
|
||||
|
||||
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
(s3, (a1, s3))
|
||||
|
||||
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
MC([a], [b])
|
||||
|
||||
|
||||
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
[((s0, (a0, s1)), ((a0, s0), s1))]
|
||||
|
||||
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
kjs = MC(MC([a], [b]), [c])
|
||||
kjs
|
||||
|
||||
|
||||
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
[((s0, (a0, s1)), (a1, ((a0, s0), s1)))]
|
||||
|
||||
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
print doc_from_stack_effect(*kjs[0])
|
||||
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
(a0 [.0.] -- [a0 .0.] a1)
|
||||
|
||||
|
||||
::
|
||||
|
||||
(a0 [.0.] -- [a0 .0.] a1)
|
||||
|
||||
a0 [.0.] a1 [cons] dip
|
||||
----------------------------
|
||||
[a0 .0.] a1
|
||||
Without any information about the contents of the quote we can't say
|
||||
much about the result.
|
||||
|
||||
I think there's a way forward. If we convert our list of terms we are
|
||||
composing into a stack structure we can use it as a *Joy expression*,
|
||||
then we can treat the *output half* of a function's stack effect comment
|
||||
as a Joy interpreter stack, and just execute combinators directly. We
|
||||
can hybridize the compostition function with an interpreter to evaluate
|
||||
combinators, compose non-combinator functions, and put type variables on
|
||||
the stack. For combinators like ``branch`` that can have more than one
|
||||
stack effect we have to "split universes" again and return both. (Note:
|
||||
bug! If one branch doesn't type check the currect code ignores it, so
|
||||
you can think things are okay but have a type error waiting in the faled
|
||||
branch, I think... D'oh! FIXME!!!)
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
|
|
@ -2924,117 +2761,6 @@ the pace I should be able to verify that conjecture by the end of June.
|
|||
|
||||
The rest of this stuff is junk and/or unfinished material.
|
||||
|
||||
``concat``
|
||||
~~~~~~~~~~
|
||||
|
||||
How to deal with ``concat``?
|
||||
|
||||
::
|
||||
|
||||
concat ([.0.] [.1.] -- [.0. .1.])
|
||||
|
||||
We would like to represent this in Python somehow...
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
concat = (S[0], S[1]), ((S[0], S[1]),)
|
||||
|
||||
But this is actually ``cons`` with the first argument restricted to be a
|
||||
stack:
|
||||
|
||||
::
|
||||
|
||||
([.0.] [.1.] -- [[.0.] .1.])
|
||||
|
||||
What we have implemented so far would actually only permit:
|
||||
|
||||
::
|
||||
|
||||
([.0.] [.1.] -- [.2.])
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
concat = (S[0], S[1]), (S[2],)
|
||||
|
||||
Which works but can lose information. Consider ``cons concat``, this is
|
||||
how much information we *could* retain:
|
||||
|
||||
::
|
||||
|
||||
(1 [.0.] [.1.] -- [1 .0. .1.])
|
||||
|
||||
As opposed to just:
|
||||
|
||||
::
|
||||
|
||||
(1 [.0.] [.1.] -- [.2.])
|
||||
|
||||
represent ``concat``
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
::
|
||||
|
||||
([.0.] [.1.] -- [A*(.0.) .1.])
|
||||
|
||||
Meaning that ``A*`` on the right-hand side should all the crap from
|
||||
``.0.``.
|
||||
|
||||
::
|
||||
|
||||
([ .0.] [.1.] -- [ A* .1.])
|
||||
([a .0.] [.1.] -- [a A* .1.])
|
||||
([a b .0.] [.1.] -- [a b A* .1.])
|
||||
([a b c .0.] [.1.] -- [a b c A* .1.])
|
||||
|
||||
or...
|
||||
|
||||
::
|
||||
|
||||
([ .0.] [.1.] -- [ .1.])
|
||||
([a .0.] [.1.] -- [a .1.])
|
||||
([a b .0.] [.1.] -- [a b .1.])
|
||||
([a b c .0.] [.1.] -- [a b c .1.])
|
||||
([a A* c .0.] [.1.] -- [a A* c .1.])
|
||||
|
||||
::
|
||||
|
||||
(a, (b, S0)) . S1 = (a, (b, (A*, S1)))
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
class Astar(object):
|
||||
def __repr__(self):
|
||||
return 'A*'
|
||||
|
||||
|
||||
def concat(s0, s1):
|
||||
a = []
|
||||
while isinstance(s0, tuple):
|
||||
term, s0 = s0
|
||||
a.append(term)
|
||||
assert isinstance(s0, StackJoyType), repr(s0)
|
||||
s1 = Astar(), s1
|
||||
for term in reversed(a):
|
||||
s1 = term, s1
|
||||
return s1
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
a, b = (A[1], S[0]), (A[2], S[1])
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
concat(a, b)
|
||||
|
||||
|
||||
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
(a1, (A*, (a2, s1)))
|
||||
|
||||
|
||||
|
||||
Appendix: Joy in the Logical Paradigm
|
||||
-------------------------------------
|
||||
|
||||
|
|
@ -3056,7 +2782,7 @@ For this to work the type label classes have to be modified to let
|
|||
|
||||
ValueError Traceback (most recent call last)
|
||||
|
||||
<ipython-input-137-4b4cb6ff86e5> in <module>()
|
||||
<ipython-input-112-4b4cb6ff86e5> in <module>()
|
||||
1 F = reduce(C, (pop, swap, roll_down, rest, rest, cons, cons))
|
||||
2
|
||||
----> 3 print doc_from_stack_effect(*F)
|
||||
|
|
@ -3174,3 +2900,221 @@ function... In any event "mixed-mode" interpreters that include values
|
|||
and type variables and can track constraints, etc. will be, uh,
|
||||
super-useful. And Abstract Interpretation should be a rich source of
|
||||
ideas.
|
||||
|
||||
Junk
|
||||
----
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
class SymbolJoyType(AnyJoyType): prefix = 'F'
|
||||
|
||||
W = map(SymbolJoyType, _R)
|
||||
|
||||
k = S[0], ((W[1], S[2]), S[0])
|
||||
Symbol('cons')
|
||||
print doc_from_stack_effect(*k)
|
||||
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
dip_a = ((W[1], S[2]), (A[1], S[0]))
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
d = relabel(S[0], dip_a)
|
||||
print doc_from_stack_effect(*d)
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
s = list(unify(d[1], k[1]))[0]
|
||||
s
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
j = update(s, k)
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
print doc_from_stack_effect(*j)
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
j
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
cons
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
for f in MC([k], [dup]):
|
||||
print doc_from_stack_effect(*f)
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
l = S[0], ((cons, S[2]), (A[1], S[0]))
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
print doc_from_stack_effect(*l)
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
|
||||
def dip_t(F):
|
||||
(quote, (a1, sec)) = F[1]
|
||||
G = F[0], sec
|
||||
P = S[3], (a1, S[3])
|
||||
a = [P]
|
||||
while isinstance(quote, tuple):
|
||||
term, quote = quote
|
||||
a.append(term)
|
||||
a.append(G)
|
||||
return a[::-1]
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
from joy.utils.stack import iter_stack
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
a, b, c = dip_t(l)
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
a
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
b
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
c
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
MC([a], [b])
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
kjs = MC(MC([a], [b]), [c])
|
||||
kjs
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
print doc_from_stack_effect(*kjs[0])
|
||||
|
||||
::
|
||||
|
||||
(a0 [.0.] -- [a0 .0.] a1)
|
||||
|
||||
a0 [.0.] a1 [cons] dip
|
||||
----------------------------
|
||||
[a0 .0.] a1
|
||||
|
||||
``concat``
|
||||
~~~~~~~~~~
|
||||
|
||||
How to deal with ``concat``?
|
||||
|
||||
::
|
||||
|
||||
concat ([.0.] [.1.] -- [.0. .1.])
|
||||
|
||||
We would like to represent this in Python somehow...
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
concat = (S[0], S[1]), ((S[0], S[1]),)
|
||||
|
||||
But this is actually ``cons`` with the first argument restricted to be a
|
||||
stack:
|
||||
|
||||
::
|
||||
|
||||
([.0.] [.1.] -- [[.0.] .1.])
|
||||
|
||||
What we have implemented so far would actually only permit:
|
||||
|
||||
::
|
||||
|
||||
([.0.] [.1.] -- [.2.])
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
concat = (S[0], S[1]), (S[2],)
|
||||
|
||||
Which works but can lose information. Consider ``cons concat``, this is
|
||||
how much information we *could* retain:
|
||||
|
||||
::
|
||||
|
||||
(1 [.0.] [.1.] -- [1 .0. .1.])
|
||||
|
||||
As opposed to just:
|
||||
|
||||
::
|
||||
|
||||
(1 [.0.] [.1.] -- [.2.])
|
||||
|
||||
represent ``concat``
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
::
|
||||
|
||||
([.0.] [.1.] -- [A*(.0.) .1.])
|
||||
|
||||
Meaning that ``A*`` on the right-hand side should all the crap from
|
||||
``.0.``.
|
||||
|
||||
::
|
||||
|
||||
([ .0.] [.1.] -- [ A* .1.])
|
||||
([a .0.] [.1.] -- [a A* .1.])
|
||||
([a b .0.] [.1.] -- [a b A* .1.])
|
||||
([a b c .0.] [.1.] -- [a b c A* .1.])
|
||||
|
||||
or...
|
||||
|
||||
::
|
||||
|
||||
([ .0.] [.1.] -- [ .1.])
|
||||
([a .0.] [.1.] -- [a .1.])
|
||||
([a b .0.] [.1.] -- [a b .1.])
|
||||
([a b c .0.] [.1.] -- [a b c .1.])
|
||||
([a A* c .0.] [.1.] -- [a A* c .1.])
|
||||
|
||||
::
|
||||
|
||||
(a, (b, S0)) . S1 = (a, (b, (A*, S1)))
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
class Astar(object):
|
||||
def __repr__(self):
|
||||
return 'A*'
|
||||
|
||||
|
||||
def concat(s0, s1):
|
||||
a = []
|
||||
while isinstance(s0, tuple):
|
||||
term, s0 = s0
|
||||
a.append(term)
|
||||
assert isinstance(s0, StackJoyType), repr(s0)
|
||||
s1 = Astar(), s1
|
||||
for term in reversed(a):
|
||||
s1 = term, s1
|
||||
return s1
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
a, b = (A[1], S[0]), (A[2], S[1])
|
||||
|
||||
.. code:: ipython2
|
||||
|
||||
concat(a, b)
|
||||
|
|
|
|||
Loading…
Reference in New Issue