From eeda5044ade9a2d601cd25211d1ab0c3a35691de Mon Sep 17 00:00:00 2001 From: Simon Forman Date: Fri, 19 Nov 2021 12:46:29 -0800 Subject: [PATCH] Using Sphinx 4.3.0 --- .../_build/html/_modules/index.html | 72 +- .../_build/html/_modules/joy/joy.html | 236 ++- .../_build/html/_modules/joy/library.html | 1694 ++++++++-------- .../_build/html/_modules/joy/parser.html | 192 +- .../html/_modules/joy/utils/pretty_print.html | 205 +- .../_build/html/_modules/joy/utils/stack.html | 296 ++- .../_build/html/_static/alabaster.css | 124 +- .../sphinx_docs/_build/html/_static/basic.css | 334 ++- .../_build/html/_static/doctools.js | 30 +- .../html/_static/documentation_options.js | 7 +- .../sphinx_docs/_build/html/_static/jquery.js | 6 +- .../_build/html/_static/pygments.css | 7 +- .../_build/html/_static/searchtools.js | 464 ++--- .../_build/html/_static/underscore.js | 37 +- docs/sphinx_docs/_build/html/genindex.html | 148 +- docs/sphinx_docs/_build/html/index.html | 128 +- docs/sphinx_docs/_build/html/joy.html | 206 +- docs/sphinx_docs/_build/html/lib.html | 696 +++---- docs/sphinx_docs/_build/html/library.html | 692 ++++--- .../_build/html/notebooks/Categorical.html | 100 +- .../Derivatives_of_Regular_Expressions.html | 716 +++---- .../_build/html/notebooks/Developing.html | 212 +- .../html/notebooks/Generator_Programs.html | 246 +-- .../_build/html/notebooks/Intro.html | 198 +- .../_build/html/notebooks/Newton-Raphson.html | 178 +- .../_build/html/notebooks/NoUpdates.html | 113 +- .../html/notebooks/Ordered_Binary_Trees.html | 556 +++-- .../_build/html/notebooks/Quadratic.html | 140 +- .../html/notebooks/Recursion_Combinators.html | 330 ++- .../_build/html/notebooks/Replacing.html | 139 +- .../html/notebooks/The_Four_Operations.html | 179 +- .../_build/html/notebooks/Treestep.html | 265 +-- .../_build/html/notebooks/TypeChecking.html | 187 +- .../_build/html/notebooks/Types.html | 1804 ++++++++--------- .../_build/html/notebooks/Zipper.html | 166 +- .../_build/html/notebooks/index.html | 97 +- docs/sphinx_docs/_build/html/objects.inv | Bin 1538 -> 1575 bytes docs/sphinx_docs/_build/html/parser.html | 132 +- docs/sphinx_docs/_build/html/pretty.html | 166 +- docs/sphinx_docs/_build/html/py-modindex.html | 68 +- docs/sphinx_docs/_build/html/search.html | 91 +- docs/sphinx_docs/_build/html/searchindex.js | 2 +- docs/sphinx_docs/_build/html/stack.html | 285 +-- docs/sphinx_docs/_build/html/types.html | 85 +- 44 files changed, 6403 insertions(+), 5626 deletions(-) diff --git a/docs/sphinx_docs/_build/html/_modules/index.html b/docs/sphinx_docs/_build/html/_modules/index.html index 60816bb..a09f508 100644 --- a/docs/sphinx_docs/_build/html/_modules/index.html +++ b/docs/sphinx_docs/_build/html/_modules/index.html @@ -1,19 +1,17 @@ - + - + - - + + Overview: module code — Thun 0.4.1 documentation - - - - - - - + + + + + + @@ -28,24 +26,48 @@
[docs]def UnaryBuiltinWrapper(f): - ''' - Wrap functions that take one argument and return a single result. - ''' - @FunctionWrapper - @wraps(f) - def inner(stack, expression, dictionary): - (a, stack) = stack - result = f(a) - return (result, stack), expression, dictionary - return inner
+ ''' + Wrap functions that take one argument and return a single result. + ''' + @FunctionWrapper + @wraps(f) + def inner(stack, expression, dictionary): + (a, stack) = stack + result = f(a) + return (result, stack), expression, dictionary + return inner
[docs]class DefinitionWrapper(object): - ''' - Provide implementation of defined functions, and some helper methods. - ''' + ''' + Provide implementation of defined functions, and some helper methods. + ''' - def __init__(self, name, body_text, doc=None): - self.name = self.__name__ = name - self.body = text_to_expression(body_text) - self._body = tuple(iter_stack(self.body)) - self.__doc__ = doc or body_text - self._compiled = None + def __init__(self, name, body_text, doc=None): + self.name = self.__name__ = name + self.body = text_to_expression(body_text) + self._body = tuple(iter_stack(self.body)) + self.__doc__ = doc or body_text + self._compiled = None - def __call__(self, stack, expression, dictionary): - if self._compiled: - return self._compiled(stack, expression, dictionary) # pylint: disable=E1102 - expression = list_to_stack(self._body, expression) - return stack, expression, dictionary + def __call__(self, stack, expression, dictionary): + if self._compiled: + return self._compiled(stack, expression, dictionary) # pylint: disable=E1102 + expression = list_to_stack(self._body, expression) + return stack, expression, dictionary -
[docs] @classmethod - def parse_definition(class_, defi): - ''' - Given some text describing a Joy function definition parse it and - return a DefinitionWrapper. - ''' - return class_(*(n.strip() for n in defi.split(None, 1)))
+
[docs] @classmethod + def parse_definition(class_, defi): + ''' + Given some text describing a Joy function definition parse it and + return a DefinitionWrapper. + ''' + # At some point I decided that the definitions file should NOT + # use '==' to separate the name from the body. But somehow the + # xerblin\gui\default_joy_home\definitions.txt file didn't get + # the memo. Nor did the load_definitions() method. + # So I think the simplest way forward at the moment will be to + # edit this function to expect '=='. -
[docs] @classmethod - def add_definitions(class_, defs, dictionary): - ''' - Scan multi-line string defs for definitions and add them to the - dictionary. - ''' - for definition in _text_to_defs(defs): - class_.add_def(definition, dictionary)
+ name, part, body = defi.partition('==') + if part: + return class_(name.strip(), body.strip()) + raise ValueError("No '==' in definition text %r" % (defi,))
-
[docs] @classmethod - def add_def(class_, definition, dictionary, fail_fails=False): - ''' - Add the definition to the dictionary. - ''' - F = class_.parse_definition(definition) - dictionary[F.name] = F
+ # return class_(*(n.strip() for n in defi.split(None, 1))) - @classmethod - def load_definitions(class_, filename, dictionary): - with open(filename) as f: - lines = [line for line in f if '==' in line] - for line in lines: - class_.add_def(line, dictionary)
+
[docs] @classmethod + def add_definitions(class_, defs, dictionary): + ''' + Scan multi-line string defs for definitions and add them to the + dictionary. + ''' + for definition in _text_to_defs(defs): + class_.add_def(definition, dictionary)
+ +
[docs] @classmethod + def add_def(class_, definition, dictionary, fail_fails=False): + ''' + Add the definition to the dictionary. + ''' + F = class_.parse_definition(definition) + dictionary[F.name] = F
+ + @classmethod + def load_definitions(class_, filename, dictionary): + with open(filename) as f: + lines = [line for line in f if '==' in line] + for line in lines: + class_.add_def(line, dictionary) def _text_to_defs(text): - return ( - line.strip() - for line in text.splitlines() - if line and not line.startswith('#') - ) + return ( + line.strip() + for line in text.splitlines() + if line + and not line.startswith('#') + and '==' in line + ) # @@ -334,356 +361,356 @@
[docs]@inscribe @FunctionWrapper def inscribe_(stack, expression, dictionary): - ''' - Create a new Joy function definition in the Joy dictionary. A - definition is given as a string with a name followed by a double - equal sign then one or more Joy functions, the body. for example: + ''' + Create a new Joy function definition in the Joy dictionary. A + definition is given as a string with a name followed by a double + equal sign then one or more Joy functions, the body. for example: - sqr == dup mul + sqr == dup mul - If you want the definition to persist over restarts, enter it into - the definitions.txt resource. - ''' - definition, stack = stack - DefinitionWrapper.add_def(definition, dictionary, fail_fails=True) - return stack, expression, dictionary
+ If you want the definition to persist over restarts, enter it into + the definitions.txt resource. + ''' + definition, stack = stack + DefinitionWrapper.add_def(definition, dictionary, fail_fails=True) + return stack, expression, dictionary
[docs]@inscribe @SimpleFunctionWrapper def parse(stack): - '''Parse the string on the stack to a Joy expression.''' - text, stack = stack - expression = text_to_expression(text) - return expression, stack
+ '''Parse the string on the stack to a Joy expression.''' + text, stack = stack + expression = text_to_expression(text) + return expression, stack # @inscribe # @SimpleFunctionWrapper # def infer_(stack): -# '''Attempt to infer the stack effect of a Joy expression.''' -# E, stack = stack -# effects = infer_expression(E) -# e = list_to_stack([(fi, (fo, ())) for fi, fo in effects]) -# return e, stack +# '''Attempt to infer the stack effect of a Joy expression.''' +# E, stack = stack +# effects = infer_expression(E) +# e = list_to_stack([(fi, (fo, ())) for fi, fo in effects]) +# return e, stack
[docs]@inscribe @SimpleFunctionWrapper def getitem(stack): - ''' - :: + ''' + :: - getitem == drop first + getitem == drop first - Expects an integer and a quote on the stack and returns the item at the - nth position in the quote counting from 0. - :: + Expects an integer and a quote on the stack and returns the item at the + nth position in the quote counting from 0. + :: - [a b c d] 0 getitem - ------------------------- - a + [a b c d] 0 getitem + ------------------------- + a - ''' - n, (Q, stack) = stack - return pick(Q, n), stack
+ ''' + n, (Q, stack) = stack + return pick(Q, n), stack
[docs]@inscribe @SimpleFunctionWrapper def drop(stack): - ''' - :: + ''' + :: - drop == [rest] times + drop == [rest] times - Expects an integer and a quote on the stack and returns the quote with - n items removed off the top. - :: + Expects an integer and a quote on the stack and returns the quote with + n items removed off the top. + :: - [a b c d] 2 drop - ---------------------- - [c d] + [a b c d] 2 drop + ---------------------- + [c d] - ''' - n, (Q, stack) = stack - while n > 0: - try: - _, Q = Q - except ValueError: - raise IndexError - n -= 1 - return Q, stack
+ ''' + n, (Q, stack) = stack + while n > 0: + try: + _, Q = Q + except ValueError: + raise IndexError + n -= 1 + return Q, stack
[docs]@inscribe @SimpleFunctionWrapper def take(stack): - ''' - Expects an integer and a quote on the stack and returns the quote with - just the top n items in reverse order (because that's easier and you can - use reverse if needed.) - :: + ''' + Expects an integer and a quote on the stack and returns the quote with + just the top n items in reverse order (because that's easier and you can + use reverse if needed.) + :: - [a b c d] 2 take - ---------------------- - [b a] + [a b c d] 2 take + ---------------------- + [b a] - ''' - n, (Q, stack) = stack - x = () - while n > 0: - try: - item, Q = Q - except ValueError: - raise IndexError - x = item, x - n -= 1 - return x, stack
+ ''' + n, (Q, stack) = stack + x = () + while n > 0: + try: + item, Q = Q + except ValueError: + raise IndexError + x = item, x + n -= 1 + return x, stack
[docs]@inscribe @SimpleFunctionWrapper def choice(stack): - ''' - Use a Boolean value to select one of two items. - :: + ''' + Use a Boolean value to select one of two items. + :: - A B False choice - ---------------------- - A + A B False choice + ---------------------- + A - A B True choice - --------------------- - B + A B True choice + --------------------- + B - Currently Python semantics are used to evaluate the "truthiness" of the - Boolean value (so empty string, zero, etc. are counted as false, etc.) - ''' - (if_, (then, (else_, stack))) = stack - return then if if_ else else_, stack
+ Currently Python semantics are used to evaluate the "truthiness" of the + Boolean value (so empty string, zero, etc. are counted as false, etc.) + ''' + (if_, (then, (else_, stack))) = stack + return then if if_ else else_, stack
[docs]@inscribe @SimpleFunctionWrapper def select(stack): - ''' - Use a Boolean value to select one of two items from a sequence. - :: + ''' + Use a Boolean value to select one of two items from a sequence. + :: - [A B] False select - ------------------------ - A + [A B] False select + ------------------------ + A - [A B] True select - ----------------------- - B + [A B] True select + ----------------------- + B - The sequence can contain more than two items but not fewer. - Currently Python semantics are used to evaluate the "truthiness" of the - Boolean value (so empty string, zero, etc. are counted as false, etc.) - ''' - (flag, (choices, stack)) = stack - (else_, (then, _)) = choices - return then if flag else else_, stack
+ The sequence can contain more than two items but not fewer. + Currently Python semantics are used to evaluate the "truthiness" of the + Boolean value (so empty string, zero, etc. are counted as false, etc.) + ''' + (flag, (choices, stack)) = stack + (else_, (then, _)) = choices + return then if flag else else_, stack
[docs]@inscribe @SimpleFunctionWrapper def max_(S): - '''Given a list find the maximum.''' - tos, stack = S - return max(iter_stack(tos)), stack
+ '''Given a list find the maximum.''' + tos, stack = S + return max(iter_stack(tos)), stack
[docs]@inscribe @SimpleFunctionWrapper def min_(S): - '''Given a list find the minimum.''' - tos, stack = S - return min(iter_stack(tos)), stack
+ '''Given a list find the minimum.''' + tos, stack = S + return min(iter_stack(tos)), stack
[docs]@inscribe @SimpleFunctionWrapper def sum_(S): - ''' - Given a quoted sequence of numbers return the sum. - :: + ''' + Given a quoted sequence of numbers return the sum. + :: - sum == 0 swap [+] step + sum == 0 swap [+] step - ''' - tos, stack = S - return sum(iter_stack(tos)), stack
+ ''' + tos, stack = S + return sum(iter_stack(tos)), stack
[docs]@inscribe @SimpleFunctionWrapper def remove(S): - ''' - Expects an item on the stack and a quote under it and removes that item - from the the quote. The item is only removed once. - :: + ''' + Expects an item on the stack and a quote under it and removes that item + from the the quote. The item is only removed once. + :: - [1 2 3 1] 1 remove - ------------------------ - [2 3 1] + [1 2 3 1] 1 remove + ------------------------ + [2 3 1] - ''' - (tos, (second, stack)) = S - l = list(iter_stack(second)) - l.remove(tos) - return list_to_stack(l), stack
+ ''' + (tos, (second, stack)) = S + l = list(iter_stack(second)) + l.remove(tos) + return list_to_stack(l), stack
[docs]@inscribe @SimpleFunctionWrapper def unique(S): - '''Given a list remove duplicate items.''' - tos, stack = S - I = list(iter_stack(tos)) - return list_to_stack(sorted(set(I), key=I.index)), stack
+ '''Given a list remove duplicate items.''' + tos, stack = S + I = list(iter_stack(tos)) + return list_to_stack(sorted(set(I), key=I.index)), stack
[docs]@inscribe @SimpleFunctionWrapper def sort_(S): - '''Given a list return it sorted.''' - tos, stack = S - return list_to_stack(sorted(iter_stack(tos))), stack
+ '''Given a list return it sorted.''' + tos, stack = S + return list_to_stack(sorted(iter_stack(tos))), stack
[docs]@inscribe @SimpleFunctionWrapper def clear(stack): - '''Clear everything from the stack. - :: + '''Clear everything from the stack. + :: - clear == stack [pop stack] loop + clear == stack [pop stack] loop - ... clear - --------------- + ... clear + --------------- - ''' - return ()
+ ''' + return ()
[docs]@inscribe @SimpleFunctionWrapper def disenstacken(stack): - ''' - The disenstacken operator expects a list on top of the stack and makes that - the stack discarding the rest of the stack. - ''' - return stack[0]
+ ''' + The disenstacken operator expects a list on top of the stack and makes that + the stack discarding the rest of the stack. + ''' + return stack[0]
[docs]@inscribe @SimpleFunctionWrapper def reverse(S): - ''' - Reverse the list on the top of the stack. - :: + ''' + Reverse the list on the top of the stack. + :: - reverse == [] swap shunt - ''' - (tos, stack) = S - res = () - for term in iter_stack(tos): - res = term, res - return res, stack
+ reverse == [] swap shunt + ''' + (tos, stack) = S + res = () + for term in iter_stack(tos): + res = term, res + return res, stack
[docs]@inscribe @SimpleFunctionWrapper def concat_(S): - ''' - Concatinate the two lists on the top of the stack. - :: + ''' + Concatinate the two lists on the top of the stack. + :: - [a b c] [d e f] concat - ---------------------------- - [a b c d e f] + [a b c] [d e f] concat + ---------------------------- + [a b c d e f] - ''' - (tos, (second, stack)) = S - return concat(second, tos), stack
+ ''' + (tos, (second, stack)) = S + return concat(second, tos), stack
[docs]@inscribe @SimpleFunctionWrapper def shunt(stack): - ''' - Like concat but reverses the top list into the second. - :: + ''' + Like concat but reverses the top list into the second. + :: - shunt == [swons] step == reverse swap concat + shunt == [swons] step == reverse swap concat - [a b c] [d e f] shunt - --------------------------- - [f e d a b c] + [a b c] [d e f] shunt + --------------------------- + [f e d a b c] - ''' - (tos, (second, stack)) = stack - while tos: - term, tos = tos - second = term, second - return second, stack
+ ''' + (tos, (second, stack)) = stack + while tos: + term, tos = tos + second = term, second + return second, stack
[docs]@inscribe @SimpleFunctionWrapper def zip_(S): - ''' - Replace the two lists on the top of the stack with a list of the pairs - from each list. The smallest list sets the length of the result list. - ''' - (tos, (second, stack)) = S - accumulator = [ - (a, (b, ())) - for a, b in zip(iter_stack(tos), iter_stack(second)) - ] - return list_to_stack(accumulator), stack
+ ''' + Replace the two lists on the top of the stack with a list of the pairs + from each list. The smallest list sets the length of the result list. + ''' + (tos, (second, stack)) = S + accumulator = [ + (a, (b, ())) + for a, b in zip(iter_stack(tos), iter_stack(second)) + ] + return list_to_stack(accumulator), stack
[docs]@inscribe @SimpleFunctionWrapper def succ(S): - '''Increment TOS.''' - (tos, stack) = S - return tos + 1, stack
+ '''Increment TOS.''' + (tos, stack) = S + return tos + 1, stack
[docs]@inscribe @SimpleFunctionWrapper def pred(S): - '''Decrement TOS.''' - (tos, stack) = S - return tos - 1, stack
+ '''Decrement TOS.''' + (tos, stack) = S + return tos - 1, stack
[docs]@inscribe @SimpleFunctionWrapper def pm(stack): - ''' - Plus or minus - :: + ''' + Plus or minus + :: - a b pm - ------------- - a+b a-b + a b pm + ------------- + a+b a-b - ''' - a, (b, stack) = stack - p, m, = b + a, b - a - return m, (p, stack)
+ ''' + a, (b, stack) = stack + p, m, = b + a, b - a + return m, (p, stack)
[docs]def floor(n): - return int(math.floor(n))
+ return int(math.floor(n)) floor.__doc__ = math.floor.__doc__ @@ -691,27 +718,27 @@
[docs]@inscribe @SimpleFunctionWrapper def divmod_(S): - ''' - divmod(x, y) -> (quotient, remainder) + ''' + divmod(x, y) -> (quotient, remainder) - Return the tuple (x//y, x%y). Invariant: div*y + mod == x. - ''' - a, (b, stack) = S - d, m = divmod(a, b) - return d, (m, stack)
+ Return the tuple (x//y, x%y). Invariant: div*y + mod == x. + ''' + a, (b, stack) = S + d, m = divmod(a, b) + return d, (m, stack)
[docs]def sqrt(a): - ''' - Return the square root of the number a. - Negative numbers return complex roots. - ''' - try: - r = math.sqrt(a) - except ValueError: - assert a < 0, repr(a) - r = math.sqrt(-a) * 1j - return r
+ ''' + Return the square root of the number a. + Negative numbers return complex roots. + ''' + try: + r = math.sqrt(a) + except ValueError: + assert a < 0, repr(a) + r = math.sqrt(-a) * 1j + return r #def execute(S): @@ -724,20 +751,20 @@
[docs]@inscribe @SimpleFunctionWrapper def id_(stack): - '''The identity function.''' - return stack
+ '''The identity function.''' + return stack
[docs]@inscribe @SimpleFunctionWrapper def void(stack): - '''True if the form on TOS is void otherwise False.''' - form, stack = stack - return _void(form), stack
+ '''True if the form on TOS is void otherwise False.''' + form, stack = stack + return _void(form), stack def _void(form): - return any(not _void(i) for i in iter_stack(form)) + return any(not _void(i) for i in iter_stack(form)) @@ -749,42 +776,42 @@
[docs]@inscribe @FunctionWrapper def words(stack, expression, dictionary): - '''Print all the words in alphabetical order.''' - print(' '.join(sorted(dictionary))) - return stack, expression, dictionary
+ '''Print all the words in alphabetical order.''' + print(' '.join(sorted(dictionary))) + return stack, expression, dictionary
[docs]@inscribe @FunctionWrapper def sharing(stack, expression, dictionary): - '''Print redistribution information.''' - print("You may convey verbatim copies of the Program's source code as" - ' you receive it, in any medium, provided that you conspicuously' - ' and appropriately publish on each copy an appropriate copyright' - ' notice; keep intact all notices stating that this License and' - ' any non-permissive terms added in accord with section 7 apply' - ' to the code; keep intact all notices of the absence of any' - ' warranty; and give all recipients a copy of this License along' - ' with the Program.' - ' You should have received a copy of the GNU General Public License' - ' along with Thun. If not see <http://www.gnu.org/licenses/>.') - return stack, expression, dictionary
+ '''Print redistribution information.''' + print("You may convey verbatim copies of the Program's source code as" + ' you receive it, in any medium, provided that you conspicuously' + ' and appropriately publish on each copy an appropriate copyright' + ' notice; keep intact all notices stating that this License and' + ' any non-permissive terms added in accord with section 7 apply' + ' to the code; keep intact all notices of the absence of any' + ' warranty; and give all recipients a copy of this License along' + ' with the Program.' + ' You should have received a copy of the GNU General Public License' + ' along with Thun. If not see <http://www.gnu.org/licenses/>.') + return stack, expression, dictionary
[docs]@inscribe @FunctionWrapper def warranty(stack, expression, dictionary): - '''Print warranty information.''' - print('THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY' - ' APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE' - ' COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM' - ' "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR' - ' IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES' - ' OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE' - ' ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS' - ' WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE' - ' COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.') - return stack, expression, dictionary
+ '''Print warranty information.''' + print('THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY' + ' APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE' + ' COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM' + ' "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR' + ' IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES' + ' OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE' + ' ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS' + ' WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE' + ' COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.') + return stack, expression, dictionary # def simple_manual(stack): @@ -809,11 +836,11 @@
[docs]@inscribe @FunctionWrapper def help_(S, expression, dictionary): - '''Accepts a quoted symbol on the top of the stack and prints its docs.''' - ((symbol, _), stack) = S - word = dictionary[symbol] - print(HELP_TEMPLATE % (symbol, getdoc(word), symbol)) - return stack, expression, dictionary
+ '''Accepts a quoted symbol on the top of the stack and prints its docs.''' + ((symbol, _), stack) = S + word = dictionary[symbol] + print(HELP_TEMPLATE % (symbol, getdoc(word), symbol)) + return stack, expression, dictionary # @@ -842,215 +869,218 @@
[docs]@inscribe @FunctionWrapper def i(stack, expression, dictionary): - ''' - The i combinator expects a quoted program on the stack and unpacks it - onto the pending expression for evaluation. - :: + ''' + The i combinator expects a quoted program on the stack and unpacks it + onto the pending expression for evaluation. + :: - [Q] i - ----------- - Q + [Q] i + ----------- + Q - ''' - quote, stack = stack - return stack, concat(quote, expression), dictionary
+ ''' + try: + quote, stack = stack + except ValueError: + raise StackUnderflowError('Not enough values on stack.') + return stack, concat(quote, expression), dictionary
[docs]@inscribe @FunctionWrapper def x(stack, expression, dictionary): - ''' - :: + ''' + :: - x == dup i + x == dup i - ... [Q] x = ... [Q] dup i - ... [Q] x = ... [Q] [Q] i - ... [Q] x = ... [Q] Q + ... [Q] x = ... [Q] dup i + ... [Q] x = ... [Q] [Q] i + ... [Q] x = ... [Q] Q - ''' - quote, _ = stack - return stack, concat(quote, expression), dictionary
+ ''' + quote, _ = stack + return stack, concat(quote, expression), dictionary
[docs]@inscribe @FunctionWrapper def b(stack, expression, dictionary): - ''' - :: + ''' + :: - b == [i] dip i + b == [i] dip i - ... [P] [Q] b == ... [P] i [Q] i - ... [P] [Q] b == ... P Q + ... [P] [Q] b == ... [P] i [Q] i + ... [P] [Q] b == ... P Q - ''' - q, (p, (stack)) = stack - return stack, concat(p, concat(q, expression)), dictionary
+ ''' + q, (p, (stack)) = stack + return stack, concat(p, concat(q, expression)), dictionary
[docs]@inscribe @FunctionWrapper def dupdip(stack, expression, dictionary): - ''' - :: + ''' + :: - [F] dupdip == dup [F] dip + [F] dupdip == dup [F] dip - ... a [F] dupdip - ... a dup [F] dip - ... a a [F] dip - ... a F a + ... a [F] dupdip + ... a dup [F] dip + ... a a [F] dip + ... a F a - ''' - F, stack = stack - a = stack[0] - return stack, concat(F, (a, expression)), dictionary
+ ''' + F, stack = stack + a = stack[0] + return stack, concat(F, (a, expression)), dictionary
[docs]@inscribe @FunctionWrapper def infra(stack, expression, dictionary): - ''' - Accept a quoted program and a list on the stack and run the program - with the list as its stack. Does not affect the rest of the stack. - :: + ''' + Accept a quoted program and a list on the stack and run the program + with the list as its stack. Does not affect the rest of the stack. + :: - ... [a b c] [Q] . infra - ----------------------------- - c b a . Q [...] swaack + ... [a b c] [Q] . infra + ----------------------------- + c b a . Q [...] swaack - ''' - (quote, (aggregate, stack)) = stack - return aggregate, concat(quote, (stack, (S_swaack, expression))), dictionary
+ ''' + (quote, (aggregate, stack)) = stack + return aggregate, concat(quote, (stack, (S_swaack, expression))), dictionary
[docs]@inscribe @FunctionWrapper def genrec(stack, expression, dictionary): - ''' - General Recursion Combinator. - :: + ''' + General Recursion Combinator. + :: - [if] [then] [rec1] [rec2] genrec - --------------------------------------------------------------------- - [if] [then] [rec1 [[if] [then] [rec1] [rec2] genrec] rec2] ifte + [if] [then] [rec1] [rec2] genrec + --------------------------------------------------------------------- + [if] [then] [rec1 [[if] [then] [rec1] [rec2] genrec] rec2] ifte - From "Recursion Theory and Joy" (j05cmp.html) by Manfred von Thun: - "The genrec combinator takes four program parameters in addition to - whatever data parameters it needs. Fourth from the top is an if-part, - followed by a then-part. If the if-part yields true, then the then-part - is executed and the combinator terminates. The other two parameters are - the rec1-part and the rec2-part. If the if-part yields false, the - rec1-part is executed. Following that the four program parameters and - the combinator are again pushed onto the stack bundled up in a quoted - form. Then the rec2-part is executed, where it will find the bundled - form. Typically it will then execute the bundled form, either with i or - with app2, or some other combinator." + From "Recursion Theory and Joy" (j05cmp.html) by Manfred von Thun: + "The genrec combinator takes four program parameters in addition to + whatever data parameters it needs. Fourth from the top is an if-part, + followed by a then-part. If the if-part yields true, then the then-part + is executed and the combinator terminates. The other two parameters are + the rec1-part and the rec2-part. If the if-part yields false, the + rec1-part is executed. Following that the four program parameters and + the combinator are again pushed onto the stack bundled up in a quoted + form. Then the rec2-part is executed, where it will find the bundled + form. Typically it will then execute the bundled form, either with i or + with app2, or some other combinator." - The way to design one of these is to fix your base case [then] and the - test [if], and then treat rec1 and rec2 as an else-part "sandwiching" - a quotation of the whole function. + The way to design one of these is to fix your base case [then] and the + test [if], and then treat rec1 and rec2 as an else-part "sandwiching" + a quotation of the whole function. - For example, given a (general recursive) function 'F': - :: + For example, given a (general recursive) function 'F': + :: - F == [I] [T] [R1] [R2] genrec + F == [I] [T] [R1] [R2] genrec - If the [I] if-part fails you must derive R1 and R2 from: - :: + If the [I] if-part fails you must derive R1 and R2 from: + :: - ... R1 [F] R2 + ... R1 [F] R2 - Just set the stack arguments in front, and figure out what R1 and R2 - have to do to apply the quoted [F] in the proper way. In effect, the - genrec combinator turns into an ifte combinator with a quoted copy of - the original definition in the else-part: - :: + Just set the stack arguments in front, and figure out what R1 and R2 + have to do to apply the quoted [F] in the proper way. In effect, the + genrec combinator turns into an ifte combinator with a quoted copy of + the original definition in the else-part: + :: - F == [I] [T] [R1] [R2] genrec - == [I] [T] [R1 [F] R2] ifte + F == [I] [T] [R1] [R2] genrec + == [I] [T] [R1 [F] R2] ifte - Primitive recursive functions are those where R2 == i. - :: + Primitive recursive functions are those where R2 == i. + :: - P == [I] [T] [R] tailrec - == [I] [T] [R [P] i] ifte - == [I] [T] [R P] ifte + P == [I] [T] [R] tailrec + == [I] [T] [R [P] i] ifte + == [I] [T] [R P] ifte - ''' - (rec2, (rec1, stack)) = stack - (then, (if_, _)) = stack - F = (if_, (then, (rec1, (rec2, (S_genrec, ()))))) - else_ = concat(rec1, (F, rec2)) - return (else_, stack), (S_ifte, expression), dictionary
+ ''' + (rec2, (rec1, stack)) = stack + (then, (if_, _)) = stack + F = (if_, (then, (rec1, (rec2, (S_genrec, ()))))) + else_ = concat(rec1, (F, rec2)) + return (else_, stack), (S_ifte, expression), dictionary
[docs]@inscribe @FunctionWrapper def map_(S, expression, dictionary): - ''' - Run the quoted program on TOS on the items in the list under it, push a - new list with the results in place of the program and original list. - ''' - # (quote, (aggregate, stack)) = S - # results = list_to_stack([ - # joy((term, stack), quote, dictionary)[0][0] - # for term in iter_stack(aggregate) - # ]) - # return (results, stack), expression, dictionary - (quote, (aggregate, stack)) = S - if not aggregate: - return (aggregate, stack), expression, dictionary - batch = () - for term in iter_stack(aggregate): - s = term, stack - batch = (s, (quote, (S_infra, (S_first, batch)))) - stack = (batch, ((), stack)) - return stack, (S_infra, expression), dictionary
+ ''' + Run the quoted program on TOS on the items in the list under it, push a + new list with the results in place of the program and original list. + ''' + # (quote, (aggregate, stack)) = S + # results = list_to_stack([ + # joy((term, stack), quote, dictionary)[0][0] + # for term in iter_stack(aggregate) + # ]) + # return (results, stack), expression, dictionary + (quote, (aggregate, stack)) = S + if not aggregate: + return (aggregate, stack), expression, dictionary + batch = () + for term in iter_stack(aggregate): + s = term, stack + batch = (s, (quote, (S_infra, (S_first, batch)))) + stack = (batch, ((), stack)) + return stack, (S_infra, expression), dictionary
[docs]@inscribe @FunctionWrapper def primrec(stack, expression, dictionary): - ''' - From the "Overview of the language JOY": + ''' + From the "Overview of the language JOY": - > The primrec combinator expects two quoted programs in addition to a - data parameter. For an integer data parameter it works like this: If - the data parameter is zero, then the first quotation has to produce - the value to be returned. If the data parameter is positive then the - second has to combine the data parameter with the result of applying - the function to its predecessor.:: + > The primrec combinator expects two quoted programs in addition to a + data parameter. For an integer data parameter it works like this: If + the data parameter is zero, then the first quotation has to produce + the value to be returned. If the data parameter is positive then the + second has to combine the data parameter with the result of applying + the function to its predecessor.:: - 5 [1] [*] primrec + 5 [1] [*] primrec - > Then primrec tests whether the top element on the stack (initially - the 5) is equal to zero. If it is, it pops it off and executes one of - the quotations, the [1] which leaves 1 on the stack as the result. - Otherwise it pushes a decremented copy of the top element and - recurses. On the way back from the recursion it uses the other - quotation, [*], to multiply what is now a factorial on top of the - stack by the second element on the stack.:: + > Then primrec tests whether the top element on the stack (initially + the 5) is equal to zero. If it is, it pops it off and executes one of + the quotations, the [1] which leaves 1 on the stack as the result. + Otherwise it pushes a decremented copy of the top element and + recurses. On the way back from the recursion it uses the other + quotation, [*], to multiply what is now a factorial on top of the + stack by the second element on the stack.:: - n [Base] [Recur] primrec + n [Base] [Recur] primrec - 0 [Base] [Recur] primrec - ------------------------------ - Base + 0 [Base] [Recur] primrec + ------------------------------ + Base - n [Base] [Recur] primrec - ------------------------------------------ n > 0 - n (n-1) [Base] [Recur] primrec Recur + n [Base] [Recur] primrec + ------------------------------------------ n > 0 + n (n-1) [Base] [Recur] primrec Recur - ''' - recur, (base, (n, stack)) = stack - if n <= 0: - expression = concat(base, expression) - else: - expression = S_primrec, concat(recur, expression) - stack = recur, (base, (n - 1, (n, stack))) - return stack, expression, dictionary
+ ''' + recur, (base, (n, stack)) = stack + if n <= 0: + expression = concat(base, expression) + else: + expression = S_primrec, concat(recur, expression) + stack = recur, (base, (n - 1, (n, stack))) + return stack, expression, dictionary #def cleave(S, expression, dictionary): @@ -1070,26 +1100,26 @@
[docs]@inscribe @FunctionWrapper def branch(stack, expression, dictionary): - ''' - Use a Boolean value to select one of two quoted programs to run. + ''' + Use a Boolean value to select one of two quoted programs to run. - :: + :: - branch == roll< choice i + branch == roll< choice i - :: + :: - False [F] [T] branch - -------------------------- - F + False [F] [T] branch + -------------------------- + F - True [F] [T] branch - ------------------------- - T + True [F] [T] branch + ------------------------- + T - ''' - (then, (else_, (flag, stack))) = stack - return stack, concat(then if flag else else_, expression), dictionary
+ ''' + (then, (else_, (flag, stack))) = stack + return stack, concat(then if flag else else_, expression), dictionary ##@inscribe @@ -1123,224 +1153,227 @@
[docs]@inscribe @FunctionWrapper def cond(stack, expression, dictionary): - ''' - This combinator works like a case statement. It expects a single quote - on the stack that must contain zero or more condition quotes and a - default quote. Each condition clause should contain a quoted predicate - followed by the function expression to run if that predicate returns - true. If no predicates return true the default function runs. + ''' + This combinator works like a case statement. It expects a single quote + on the stack that must contain zero or more condition quotes and a + default quote. Each condition clause should contain a quoted predicate + followed by the function expression to run if that predicate returns + true. If no predicates return true the default function runs. - It works by rewriting into a chain of nested `ifte` expressions, e.g.:: + It works by rewriting into a chain of nested `ifte` expressions, e.g.:: - [[[B0] T0] [[B1] T1] [D]] cond - ----------------------------------------- - [B0] [T0] [[B1] [T1] [D] ifte] ifte + [[[B0] T0] [[B1] T1] [D]] cond + ----------------------------------------- + [B0] [T0] [[B1] [T1] [D] ifte] ifte - ''' - conditions, stack = stack - if conditions: - expression = _cond(conditions, expression) - try: - # Attempt to preload the args to first ifte. - (P, (T, (E, expression))) = expression - except ValueError: - # If, for any reason, the argument to cond should happen to contain - # only the default clause then this optimization will fail. - pass - else: - stack = (E, (T, (P, stack))) - return stack, expression, dictionary
+ ''' + conditions, stack = stack + if conditions: + expression = _cond(conditions, expression) + try: + # Attempt to preload the args to first ifte. + (P, (T, (E, expression))) = expression + except ValueError: + # If, for any reason, the argument to cond should happen to contain + # only the default clause then this optimization will fail. + pass + else: + stack = (E, (T, (P, stack))) + return stack, expression, dictionary def _cond(conditions, expression): - (clause, rest) = conditions - if not rest: # clause is [D] - return clause - P, T = clause - return (P, (T, (_cond(rest, ()), (S_ifte, expression)))) + (clause, rest) = conditions + if not rest: # clause is [D] + return clause + P, T = clause + return (P, (T, (_cond(rest, ()), (S_ifte, expression))))
[docs]@inscribe @FunctionWrapper def dip(stack, expression, dictionary): - ''' - The dip combinator expects a quoted program on the stack and below it - some item, it hoists the item into the expression and runs the program - on the rest of the stack. - :: + ''' + The dip combinator expects a quoted program on the stack and below it + some item, it hoists the item into the expression and runs the program + on the rest of the stack. + :: - ... x [Q] dip - ------------------- - ... Q x + ... x [Q] dip + ------------------- + ... Q x - ''' - (quote, (x, stack)) = stack - expression = (x, expression) - return stack, concat(quote, expression), dictionary
+ ''' + try: + (quote, (x, stack)) = stack + except ValueError: + raise StackUnderflowError('Not enough values on stack.') + expression = (x, expression) + return stack, concat(quote, expression), dictionary
[docs]@inscribe @FunctionWrapper def dipd(S, expression, dictionary): - ''' - Like dip but expects two items. - :: + ''' + Like dip but expects two items. + :: - ... y x [Q] dip - --------------------- - ... Q y x + ... y x [Q] dip + --------------------- + ... Q y x - ''' - (quote, (x, (y, stack))) = S - expression = (y, (x, expression)) - return stack, concat(quote, expression), dictionary
+ ''' + (quote, (x, (y, stack))) = S + expression = (y, (x, expression)) + return stack, concat(quote, expression), dictionary
[docs]@inscribe @FunctionWrapper def dipdd(S, expression, dictionary): - ''' - Like dip but expects three items. - :: + ''' + Like dip but expects three items. + :: - ... z y x [Q] dip - ----------------------- - ... Q z y x + ... z y x [Q] dip + ----------------------- + ... Q z y x - ''' - (quote, (x, (y, (z, stack)))) = S - expression = (z, (y, (x, expression))) - return stack, concat(quote, expression), dictionary
+ ''' + (quote, (x, (y, (z, stack)))) = S + expression = (z, (y, (x, expression))) + return stack, concat(quote, expression), dictionary
[docs]@inscribe @FunctionWrapper def app1(S, expression, dictionary): - ''' - Given a quoted program on TOS and anything as the second stack item run - the program and replace the two args with the first result of the - program. - :: + ''' + Given a quoted program on TOS and anything as the second stack item run + the program and replace the two args with the first result of the + program. + :: - ... x [Q] . app1 - ----------------------------------- - ... [x ...] [Q] . infra first + ... x [Q] . app1 + ----------------------------------- + ... [x ...] [Q] . infra first - ''' - (quote, (x, stack)) = S - stack = (quote, ((x, stack), stack)) - expression = (S_infra, (S_first, expression)) - return stack, expression, dictionary
+ ''' + (quote, (x, stack)) = S + stack = (quote, ((x, stack), stack)) + expression = (S_infra, (S_first, expression)) + return stack, expression, dictionary
[docs]@inscribe @FunctionWrapper def app2(S, expression, dictionary): - '''Like app1 with two items. - :: + '''Like app1 with two items. + :: - ... y x [Q] . app2 - ----------------------------------- - ... [y ...] [Q] . infra first - [x ...] [Q] infra first + ... y x [Q] . app2 + ----------------------------------- + ... [y ...] [Q] . infra first + [x ...] [Q] infra first - ''' - (quote, (x, (y, stack))) = S - expression = (S_infra, (S_first, - ((x, stack), (quote, (S_infra, (S_first, - expression)))))) - stack = (quote, ((y, stack), stack)) - return stack, expression, dictionary
+ ''' + (quote, (x, (y, stack))) = S + expression = (S_infra, (S_first, + ((x, stack), (quote, (S_infra, (S_first, + expression)))))) + stack = (quote, ((y, stack), stack)) + return stack, expression, dictionary
[docs]@inscribe @FunctionWrapper def app3(S, expression, dictionary): - '''Like app1 with three items. - :: + '''Like app1 with three items. + :: - ... z y x [Q] . app3 - ----------------------------------- - ... [z ...] [Q] . infra first - [y ...] [Q] infra first - [x ...] [Q] infra first + ... z y x [Q] . app3 + ----------------------------------- + ... [z ...] [Q] . infra first + [y ...] [Q] infra first + [x ...] [Q] infra first - ''' - (quote, (x, (y, (z, stack)))) = S - expression = (S_infra, (S_first, - ((y, stack), (quote, (S_infra, (S_first, - ((x, stack), (quote, (S_infra, (S_first, - expression)))))))))) - stack = (quote, ((z, stack), stack)) - return stack, expression, dictionary
+ ''' + (quote, (x, (y, (z, stack)))) = S + expression = (S_infra, (S_first, + ((y, stack), (quote, (S_infra, (S_first, + ((x, stack), (quote, (S_infra, (S_first, + expression)))))))))) + stack = (quote, ((z, stack), stack)) + return stack, expression, dictionary
[docs]@inscribe @FunctionWrapper def step(S, expression, dictionary): - ''' - Run a quoted program on each item in a sequence. - :: + ''' + Run a quoted program on each item in a sequence. + :: - ... [] [Q] . step - ----------------------- - ... . + ... [] [Q] . step + ----------------------- + ... . - ... [a] [Q] . step - ------------------------ - ... a . Q + ... [a] [Q] . step + ------------------------ + ... a . Q - ... [a b c] [Q] . step - ---------------------------------------- - ... a . Q [b c] [Q] step + ... [a b c] [Q] . step + ---------------------------------------- + ... a . Q [b c] [Q] step - The step combinator executes the quotation on each member of the list - on top of the stack. - ''' - (quote, (aggregate, stack)) = S - if not aggregate: - return stack, expression, dictionary - head, tail = aggregate - stack = quote, (head, stack) - if tail: - expression = tail, (quote, (S_step, expression)) - expression = S_i, expression - return stack, expression, dictionary
+ The step combinator executes the quotation on each member of the list + on top of the stack. + ''' + (quote, (aggregate, stack)) = S + if not aggregate: + return stack, expression, dictionary + head, tail = aggregate + stack = quote, (head, stack) + if tail: + expression = tail, (quote, (S_step, expression)) + expression = S_i, expression + return stack, expression, dictionary
[docs]@inscribe @FunctionWrapper def times(stack, expression, dictionary): - ''' - times == [-- dip] cons [swap] infra [0 >] swap while pop - :: + ''' + times == [-- dip] cons [swap] infra [0 >] swap while pop + :: - ... n [Q] . times - --------------------- w/ n <= 0 - ... . + ... n [Q] . times + --------------------- w/ n <= 0 + ... . - ... 1 [Q] . times - ----------------------- - ... . Q + ... 1 [Q] . times + ----------------------- + ... . Q - ... n [Q] . times - ------------------------------------- w/ n > 1 - ... . Q (n - 1) [Q] times + ... n [Q] . times + ------------------------------------- w/ n > 1 + ... . Q (n - 1) [Q] times - ''' - # times == [-- dip] cons [swap] infra [0 >] swap while pop - (quote, (n, stack)) = stack - if n <= 0: - return stack, expression, dictionary - n -= 1 - if n: - expression = n, (quote, (S_times, expression)) - expression = concat(quote, expression) - return stack, expression, dictionary
+ ''' + # times == [-- dip] cons [swap] infra [0 >] swap while pop + (quote, (n, stack)) = stack + if n <= 0: + return stack, expression, dictionary + n -= 1 + if n: + expression = n, (quote, (S_times, expression)) + expression = concat(quote, expression) + return stack, expression, dictionary # The current definition above works like this: @@ -1362,48 +1395,57 @@
[docs]@inscribe @FunctionWrapper def loop(stack, expression, dictionary): - ''' - Basic loop combinator. - :: + ''' + Basic loop combinator. + :: - ... True [Q] loop - ----------------------- - ... Q [Q] loop + ... True [Q] loop + ----------------------- + ... Q [Q] loop - ... False [Q] loop - ------------------------ - ... + ... False [Q] loop + ------------------------ + ... - ''' - quote, (flag, stack) = stack - if flag: - expression = concat(quote, (quote, (S_loop, expression))) - return stack, expression, dictionary
+ ''' + try: + quote, stack = stack + except ValueError: + raise StackUnderflowError('Not enough values on stack.') + if not isinstance(quote, tuple): + raise NotAListError('Loop body not a list.') + try: + (flag, stack) = stack + except ValueError: + raise StackUnderflowError('Not enough values on stack.') + if flag: + expression = concat(quote, (quote, (S_loop, expression))) + return stack, expression, dictionary
[docs]@inscribe @FunctionWrapper def cmp_(stack, expression, dictionary): - ''' - cmp takes two values and three quoted programs on the stack and runs - one of the three depending on the results of comparing the two values: - :: + ''' + cmp takes two values and three quoted programs on the stack and runs + one of the three depending on the results of comparing the two values: + :: - a b [G] [E] [L] cmp - ------------------------- a > b - G + a b [G] [E] [L] cmp + ------------------------- a > b + G - a b [G] [E] [L] cmp - ------------------------- a = b - E + a b [G] [E] [L] cmp + ------------------------- a = b + E - a b [G] [E] [L] cmp - ------------------------- a < b - L - ''' - L, (E, (G, (b, (a, stack)))) = stack - expression = concat(G if a > b else L if a < b else E, expression) - return stack, expression, dictionary
+ a b [G] [E] [L] cmp + ------------------------- a < b + L + ''' + L, (E, (G, (b, (a, stack)))) = stack + expression = concat(G if a > b else L if a < b else E, expression) + return stack, expression, dictionary # FunctionWrapper(cleave), @@ -1412,46 +1454,46 @@ for F in ( - #divmod_ = pm = __(n2, n1), __(n4, n3) + #divmod_ = pm = __(n2, n1), __(n4, n3) - BinaryBuiltinWrapper(operator.eq), - BinaryBuiltinWrapper(operator.ge), - BinaryBuiltinWrapper(operator.gt), - BinaryBuiltinWrapper(operator.le), - BinaryBuiltinWrapper(operator.lt), - BinaryBuiltinWrapper(operator.ne), + BinaryBuiltinWrapper(operator.eq), + BinaryBuiltinWrapper(operator.ge), + BinaryBuiltinWrapper(operator.gt), + BinaryBuiltinWrapper(operator.le), + BinaryBuiltinWrapper(operator.lt), + BinaryBuiltinWrapper(operator.ne), - BinaryBuiltinWrapper(operator.xor), - BinaryBuiltinWrapper(operator.lshift), - BinaryBuiltinWrapper(operator.rshift), + BinaryBuiltinWrapper(operator.xor), + BinaryBuiltinWrapper(operator.lshift), + BinaryBuiltinWrapper(operator.rshift), - BinaryBuiltinWrapper(operator.and_), - BinaryBuiltinWrapper(operator.or_), + BinaryBuiltinWrapper(operator.and_), + BinaryBuiltinWrapper(operator.or_), - BinaryBuiltinWrapper(operator.add), - BinaryBuiltinWrapper(operator.floordiv), - BinaryBuiltinWrapper(operator.mod), - BinaryBuiltinWrapper(operator.mul), - BinaryBuiltinWrapper(operator.pow), - BinaryBuiltinWrapper(operator.sub), - BinaryBuiltinWrapper(operator.truediv), + BinaryBuiltinWrapper(operator.add), + BinaryBuiltinWrapper(operator.floordiv), + BinaryBuiltinWrapper(operator.mod), + BinaryBuiltinWrapper(operator.mul), + BinaryBuiltinWrapper(operator.pow), + BinaryBuiltinWrapper(operator.sub), +## BinaryBuiltinWrapper(operator.truediv), - UnaryBuiltinWrapper(bool), - UnaryBuiltinWrapper(operator.not_), + UnaryBuiltinWrapper(bool), + UnaryBuiltinWrapper(operator.not_), - UnaryBuiltinWrapper(abs), - UnaryBuiltinWrapper(operator.neg), - UnaryBuiltinWrapper(sqrt), + UnaryBuiltinWrapper(abs), + UnaryBuiltinWrapper(operator.neg), + UnaryBuiltinWrapper(sqrt), - UnaryBuiltinWrapper(floor), - UnaryBuiltinWrapper(round), - ): - inscribe(F) + UnaryBuiltinWrapper(floor), + UnaryBuiltinWrapper(round), + ): + inscribe(F) del F # Otherwise Sphinx autodoc will pick it up. for name, primitive in getmembers(genlib, isfunction): - inscribe(SimpleFunctionWrapper(primitive)) + inscribe(SimpleFunctionWrapper(primitive)) add_aliases(_dictionary, ALIASES) @@ -1461,10 +1503,34 @@ + + diff --git a/docs/sphinx_docs/_build/html/lib.html b/docs/sphinx_docs/_build/html/lib.html index 6903842..7783c89 100644 --- a/docs/sphinx_docs/_build/html/lib.html +++ b/docs/sphinx_docs/_build/html/lib.html @@ -1,19 +1,18 @@ - + - + - - + + + Functions Grouped by, er, Function with Examples — Thun 0.4.1 documentation - - - - - - - + + + + + + @@ -30,14 +29,16 @@
+ +
-
+

Functions Grouped by, er, Function with Examples

-
from notebook_preamble import J, V
+
from notebook_preamble import J, V
 
-
+

Stack Chatter

This is what I like to call the functions that just rearrange things on the stack. (One thing I want to mention is that during a hypothetical @@ -45,144 +46,144 @@ compilation phase these “stack chatter” words effectively disappear because we can map the logical stack locations to registers that remain static for the duration of the computation. This remains to be done but it’s “off the shelf” technology.)

-
+

clear

-
J('1 2 3 clear')
+
J('1 2 3 clear')
 
(nothing)
 
-
-
+
+

dup dupd

-
J('1 2 3 dup')
+
J('1 2 3 dup')
 
1 2 3 3
 
-
J('1 2 3 dupd')
+
J('1 2 3 dupd')
 
1 2 2 3
 
-
-
+
+

enstacken disenstacken stack unstack

(I may have these paired up wrong. I.e. disenstacken should be unstack and vice versa.)

-
J('1 2 3 enstacken') # Replace the stack with a quote of itself.
+
J('1 2 3 enstacken') # Replace the stack with a quote of itself.
 
[3 2 1]
 
-
J('4 5 6 [3 2 1] disenstacken')  # Unpack a list onto the stack.
+
J('4 5 6 [3 2 1] disenstacken')  # Unpack a list onto the stack.
 
4 5 6 3 2 1
 
-
J('1 2 3 stack')  # Get the stack on the stack.
+
J('1 2 3 stack')  # Get the stack on the stack.
 
1 2 3 [3 2 1]
 
-
J('1 2 3 [4 5 6] unstack')  # Replace the stack with the list on top.
-                            # The items appear reversed but they are not,
-                            # 4 is on the top of both the list and the stack.
+
J('1 2 3 [4 5 6] unstack')  # Replace the stack with the list on top.
+                            # The items appear reversed but they are not,
+                            # 4 is on the top of both the list and the stack.
 
6 5 4
 
-
-
+
+

pop popd popop

-
J('1 2 3 pop')
+
J('1 2 3 pop')
 
1 2
 
-
J('1 2 3 popd')
+
J('1 2 3 popd')
 
1 3
 
-
J('1 2 3 popop')
+
J('1 2 3 popop')
 
1
 
-
-
+
+

roll< rolldown roll> rollup

The “down” and “up” refer to the movement of two of the top three items (displacing the third.)

-
J('1 2 3 roll<')
+
J('1 2 3 roll<')
 
2 3 1
 
-
J('1 2 3 roll>')
+
J('1 2 3 roll>')
 
3 1 2
 
-
-
+
+

swap

-
J('1 2 3 swap')
+
J('1 2 3 swap')
 
1 3 2
 
-
-
+
+

tuck over

-
J('1 2 3 tuck')
+
J('1 2 3 tuck')
 
1 3 2 3
 
-
J('1 2 3 over')
+
J('1 2 3 over')
 
1 2 3 2
 
-
-
+
+

unit quoted unquoted

-
J('1 2 3 unit')
+
J('1 2 3 unit')
 
1 2 [3]
 
-
J('1 2 3 quoted')
+
J('1 2 3 quoted')
 
1 [2] 3
 
-
J('1 [2] 3 unquoted')
+
J('1 [2] 3 unquoted')
 
1 2 3
 
-
V('1 [dup] 3 unquoted')  # Unquoting evaluates.  Be aware.
+
V('1 [dup] 3 unquoted')  # Unquoting evaluates.  Be aware.
 
              . 1 [dup] 3 unquoted
@@ -197,447 +198,447 @@ it’s “off the shelf” technology.)

1 1 3 .
-
-
-
+
+
+

List words

-
+

concat swoncat shunt

-
J('[1 2 3] [4 5 6] concat')
+
J('[1 2 3] [4 5 6] concat')
 
[1 2 3 4 5 6]
 
-
J('[1 2 3] [4 5 6] swoncat')
+
J('[1 2 3] [4 5 6] swoncat')
 
[4 5 6 1 2 3]
 
-
J('[1 2 3] [4 5 6] shunt')
+
J('[1 2 3] [4 5 6] shunt')
 
[6 5 4 1 2 3]
 
-
-
+
+

cons swons uncons

-
J('1 [2 3] cons')
+
J('1 [2 3] cons')
 
[1 2 3]
 
-
J('[2 3] 1 swons')
+
J('[2 3] 1 swons')
 
[1 2 3]
 
-
J('[1 2 3] uncons')
+
J('[1 2 3] uncons')
 
1 [2 3]
 
-
-
+
+

first second third rest

-
J('[1 2 3 4] first')
+
J('[1 2 3 4] first')
 
1
 
-
J('[1 2 3 4] second')
+
J('[1 2 3 4] second')
 
2
 
-
J('[1 2 3 4] third')
+
J('[1 2 3 4] third')
 
3
 
-
J('[1 2 3 4] rest')
+
J('[1 2 3 4] rest')
 
[2 3 4]
 
-
-
+
+

flatten

-
J('[[1] [2 [3] 4] [5 6]] flatten')
+
J('[[1] [2 [3] 4] [5 6]] flatten')
 
[1 2 [3] 4 5 6]
 
-
-
+
+

getitem at of drop take

at and getitem are the same function. of == swap at

-
J('[10 11 12 13 14] 2 getitem')
+
J('[10 11 12 13 14] 2 getitem')
 
12
 
-
J('[1 2 3 4] 0 at')
+
J('[1 2 3 4] 0 at')
 
1
 
-
J('2 [1 2 3 4] of')
+
J('2 [1 2 3 4] of')
 
3
 
-
J('[1 2 3 4] 2 drop')
+
J('[1 2 3 4] 2 drop')
 
[3 4]
 
-
J('[1 2 3 4] 2 take')  # reverses the order
+
J('[1 2 3 4] 2 take')  # reverses the order
 
[2 1]
 

reverse could be defines as reverse == dup size take

-
-
+
+

remove

-
J('[1 2 3 1 4] 1 remove')
+
J('[1 2 3 1 4] 1 remove')
 
[2 3 1 4]
 
-
-
+
+

reverse

-
J('[1 2 3 4] reverse')
+
J('[1 2 3 4] reverse')
 
[4 3 2 1]
 
-
-
+
+

size

-
J('[1 1 1 1] size')
+
J('[1 1 1 1] size')
 
4
 
-
-
+
+

swaack

“Swap stack” swap the list on the top of the stack for the stack, and put the old stack on top of the new one. Think of it as a context switch. Niether of the lists/stacks change their order.

-
J('1 2 3 [4 5 6] swaack')
+
J('1 2 3 [4 5 6] swaack')
 
6 5 4 [3 2 1]
 
-
-
+
+

choice select

-
J('23 9 1 choice')
+
J('23 9 1 choice')
 
9
 
-
J('23 9 0 choice')
+
J('23 9 0 choice')
 
23
 
-
J('[23 9 7] 1 select')  # select is basically getitem, should retire it?
+
J('[23 9 7] 1 select')  # select is basically getitem, should retire it?
 
9
 
-
J('[23 9 7] 0 select')
+
J('[23 9 7] 0 select')
 
23
 
-
-
+
+

zip

-
J('[1 2 3] [6 5 4] zip')
+
J('[1 2 3] [6 5 4] zip')
 
[[6 1] [5 2] [4 3]]
 
-
J('[1 2 3] [6 5 4] zip [sum] map')
+
J('[1 2 3] [6 5 4] zip [sum] map')
 
[7 7 7]
 
-
-
-
+
+
+

Math words

-
+

+ add

-
J('23 9 +')
+
J('23 9 +')
 
32
 
-
-
+
+

- sub

-
J('23 9 -')
+
J('23 9 -')
 
14
 
-
-
+
+

* mul

-
J('23 9 *')
+
J('23 9 *')
 
207
 
-
-
+
+

/ div floordiv truediv

-
J('23 9 /')
+
J('23 9 /')
 
2.5555555555555554
 
-
J('23 -9 truediv')
+
J('23 -9 truediv')
 
-2.5555555555555554
 
-
J('23 9 div')
+
J('23 9 div')
 
2
 
-
J('23 9 floordiv')
+
J('23 9 floordiv')
 
2
 
-
J('23 -9 div')
+
J('23 -9 div')
 
-3
 
-
J('23 -9 floordiv')
+
J('23 -9 floordiv')
 
-3
 
-
-
+
+

% mod modulus rem remainder

-
J('23 9 %')
+
J('23 9 %')
 
5
 
-
-
+
+

neg

-
J('23 neg -5 neg')
+
J('23 neg -5 neg')
 
-23 5
 
-
-
+
+

pow

-
J('2 10 pow')
+
J('2 10 pow')
 
1024
 
-
-
+
+

sqr sqrt

-
J('23 sqr')
+
J('23 sqr')
 
529
 
-
J('23 sqrt')
+
J('23 sqrt')
 
4.795831523312719
 
-
-
+
+

++ succ -- pred

-
J('1 ++')
+
J('1 ++')
 
2
 
-
J('1 --')
+
J('1 --')
 
0
 
-
-
+
+

<< lshift >> rshift

-
J('8 1 <<')
+
J('8 1 <<')
 
16
 
-
J('8 1 >>')
+
J('8 1 >>')
 
4
 
-
-
+
+

average

-
J('[1 2 3 5] average')
+
J('[1 2 3 5] average')
 
2.75
 
-
-
+
+

range range_to_zero down_to_zero

-
J('5 range')
+
J('5 range')
 
[4 3 2 1 0]
 
-
J('5 range_to_zero')
+
J('5 range_to_zero')
 
[0 1 2 3 4 5]
 
-
J('5 down_to_zero')
+
J('5 down_to_zero')
 
5 4 3 2 1 0
 
-
-
+
+

product

-
J('[1 2 3 5] product')
+
J('[1 2 3 5] product')
 
30
 
-
-
+
+

sum

-
J('[1 2 3 5] sum')
+
J('[1 2 3 5] sum')
 
11
 
-
-
+
+

min

-
J('[1 2 3 5] min')
+
J('[1 2 3 5] min')
 
1
 
-
-
+
+

gcd

-
J('45 30 gcd')
+
J('45 30 gcd')
 
15
 
-
-
+
+

least_fraction

If we represent fractions as a quoted pair of integers [q d] this word reduces them to their … least common factors or whatever.

-
J('[45 30] least_fraction')
+
J('[45 30] least_fraction')
 
[3 2]
 
-
J('[23 12] least_fraction')
+
J('[23 12] least_fraction')
 
[23 12]
 
-
-
-
+
+
+

Logic and Comparison

-
+

? truthy

Get the Boolean value of the item on the top of the stack.

-
J('23 truthy')
+
J('23 truthy')
 
True
 
-
J('[] truthy')  # Python semantics.
+
J('[] truthy')  # Python semantics.
 
False
 
-
J('0 truthy')
+
J('0 truthy')
 
False
@@ -646,7 +647,7 @@ reduces them to their … least common factors or whatever.

? == dup truthy
 
-
V('23 ?')
+
V('23 ?')
 
        . 23 ?
@@ -656,31 +657,31 @@ reduces them to their … least common factors or whatever.

23 True .
-
J('[] ?')
+
J('[] ?')
 
[] False
 
-
J('0 ?')
+
J('0 ?')
 
0 False
 
-
-
+
+

& and

-
J('23 9 &')
+
J('23 9 &')
 
1
 
-
-
+
+

!= <> ne

-
J('23 9 !=')
+
J('23 9 !=')
 
True
@@ -690,65 +691,65 @@ reduces them to their … least common factors or whatever.

The usual suspects: - < lt - <= le
- = eq - > gt - >= ge - not - or
-
-
+
+

^ xor

-
J('1 1 ^')
+
J('1 1 ^')
 
0
 
-
J('1 0 ^')
+
J('1 0 ^')
 
1
 
-
-
-
+
+
+

Miscellaneous

-
+

help

-
J('[help] help')
+
J('[help] help')
 
Accepts a quoted symbol on the top of the stack and prints its docs.
 
-
-
+
+

parse

-
J('[parse] help')
+
J('[parse] help')
 
Parse the string on the stack to a Joy expression.
 
-
J('1 "2 [3] dup" parse')
+
J('1 "2 [3] dup" parse')
 
1 [2 [3] dup]
 
-
-
+
+

run

Evaluate a quoted Joy sequence.

-
J('[1 2 dup + +] run')
+
J('[1 2 dup + +] run')
 
[5]
 
-
-
-
+
+
+

Combinators

-
+

app1 app2 app3

-
J('[app1] help')
+
J('[app1] help')
 
Given a quoted program on TOS and anything as the second stack item run
@@ -760,19 +761,19 @@ reduces them to their … least common factors or whatever.

... [x ...] [Q] . infra first
-
J('10 4 [sqr *] app1')
+
J('10 4 [sqr *] app1')
 
10 160
 
-
J('10 3 4 [sqr *] app2')
+
J('10 3 4 [sqr *] app2')
 
10 90 160
 
-
J('[app2] help')
+
J('[app2] help')
 
Like app1 with two items.
@@ -783,14 +784,14 @@ reduces them to their … least common factors or whatever.

[x ...] [Q] infra first
-
J('10 2 3 4 [sqr *] app3')
+
J('10 2 3 4 [sqr *] app3')
 
10 40 90 160
 
-
-
+
+

anamorphism

Given an initial value, a predicate function [P], and a generator function [G], the anamorphism combinator creates a sequence.

@@ -803,29 +804,29 @@ function [G]
range == [0 <=] [1 - dup] anamorphism
 
-
J('3 [0 <=] [1 - dup] anamorphism')
+
J('3 [0 <=] [1 - dup] anamorphism')
 
[2 1 0]
 
-
-
+
+

branch

-
J('3 4 1 [+] [*] branch')
+
J('3 4 1 [+] [*] branch')
 
12
 
-
J('3 4 0 [+] [*] branch')
+
J('3 4 0 [+] [*] branch')
 
7
 
-
-
+
+

cleave

... x [P] [Q] cleave
 
@@ -845,42 +846,42 @@ in terms of app2
cleave == [i] app2 [popd] dip
 
-
J('10 2 [+] [-] cleave')
+
J('10 2 [+] [-] cleave')
 
10 12 8
 
-
-
+
+

dip dipd dipdd

-
J('1 2 3 4 5 [+] dip')
+
J('1 2 3 4 5 [+] dip')
 
1 2 7 5
 
-
J('1 2 3 4 5 [+] dipd')
+
J('1 2 3 4 5 [+] dipd')
 
1 5 4 5
 
-
J('1 2 3 4 5 [+] dipdd')
+
J('1 2 3 4 5 [+] dipdd')
 
3 3 4 5
 
-
-
+
+

dupdip

Expects a quoted program [Q] on the stack and some item under it, dup the item and dip the quoted program under it.

n [Q] dupdip == n Q n
 
-
V('23 [++] dupdip *')  # N(N + 1)
+
V('23 [++] dupdip *')  # N(N + 1)
 
        . 23 [++] dupdip *
@@ -892,10 +893,10 @@ in terms of app2552 .
 
-
-
+
+

genrec primrec

-
J('[genrec] help')
+
J('[genrec] help')
 
General Recursion Combinator.
@@ -944,16 +945,16 @@ in terms of app2)
 
-
J('3 [1 <=] [] [dup --] [i *] genrec')
+
J('3 [1 <=] [] [dup --] [i *] genrec')
 
6
 
-
-
+
+

i

-
V('1 2 3 [+ +] i')
+
V('1 2 3 [+ +] i')
 
            . 1 2 3 [+ +] i
@@ -966,28 +967,28 @@ in terms of app26 .
 
-
-
+
+

ifte

[predicate] [then] [else] ifte
 
-
J('1 2 [1] [+] [*] ifte')
+
J('1 2 [1] [+] [*] ifte')
 
3
 
-
J('1 2 [0] [+] [*] ifte')
+
J('1 2 [0] [+] [*] ifte')
 
2
 
-
-
+
+

infra

-
V('1 2 3 [4 5 6] [* +] infra')
+
V('1 2 3 [4 5 6] [* +] infra')
 
                    . 1 2 3 [4 5 6] [* +] infra
@@ -1003,10 +1004,10 @@ in terms of app21 2 3 [26] .
 
-
-
+
+

loop

-
J('[loop] help')
+
J('[loop] help')
 
Basic loop combinator.
@@ -1020,7 +1021,7 @@ in terms of app2...
 
-
V('3 dup [1 - dup] loop')
+
V('3 dup [1 - dup] loop')
 
              . 3 dup [1 - dup] loop
@@ -1045,54 +1046,54 @@ in terms of app20 .
 
-
-
+
+

map pam

-
J('10 [1 2 3] [*] map')
+
J('10 [1 2 3] [*] map')
 
10 [10 20 30]
 
-
J('10 5 [[*][/][+][-]] pam')
+
J('10 5 [[*][/][+][-]] pam')
 
10 5 [50 2.0 15 5]
 
-
-
+
+

nullary unary binary ternary

Run a quoted program enforcing arity.

-
J('1 2 3 4 5 [+] nullary')
+
J('1 2 3 4 5 [+] nullary')
 
1 2 3 4 5 9
 
-
J('1 2 3 4 5 [+] unary')
+
J('1 2 3 4 5 [+] unary')
 
1 2 3 4 9
 
-
J('1 2 3 4 5 [+] binary')  # + has arity 2 so this is technically pointless...
+
J('1 2 3 4 5 [+] binary')  # + has arity 2 so this is technically pointless...
 
1 2 3 9
 
-
J('1 2 3 4 5 [+] ternary')
+
J('1 2 3 4 5 [+] ternary')
 
1 2 9
 
-
-
+
+

step

-
J('[step] help')
+
J('[step] help')
 
Run a quoted program on each item in a sequence.
@@ -1115,7 +1116,7 @@ in terms of app2on top of the stack.
 
-
V('0 [1 2 3] [+] step')
+
V('0 [1 2 3] [+] step')
 
              . 0 [1 2 3] [+] step
@@ -1137,10 +1138,10 @@ in terms of app26 .
 
-
-
+
+

times

-
V('3 2 1 2 [+] times')
+
V('3 2 1 2 [+] times')
 
            . 3 2 1 2 [+] times
@@ -1157,10 +1158,10 @@ in terms of app26 .
 
-
-
+
+

b

-
J('[b] help')
+
J('[b] help')
 
b == [i] dip i
@@ -1169,7 +1170,7 @@ in terms of app2... [P] [Q] b == ... P Q
 
-
V('1 2 [3] [4] b')
+
V('1 2 [3] [4] b')
 
            . 1 2 [3] [4] b
@@ -1182,22 +1183,22 @@ in terms of app21 2 3 4 .
 
-
-
+
+

while

[predicate] [body] while
 
-
J('3 [0 >] [dup --] while')
+
J('3 [0 >] [dup --] while')
 
3 2 1 0
 
-
-
+
+

x

-
J('[x] help')
+
J('[x] help')
 
x == dup i
@@ -1207,7 +1208,7 @@ in terms of app2... [Q] x = ... [Q]  Q
 
-
V('1 [2] [i 3] x')  # Kind of a pointless example.
+
V('1 [2] [i 3] x')  # Kind of a pointless example.
 
            . 1 [2] [i 3] x
@@ -1222,133 +1223,79 @@ in terms of app21 2 3 3 .
 
-
-
-
+
+
+

void

Implements **Laws of Form** *arithmetic* over quote-only datastructures (that is, datastructures that consist soley of containers, without strings or numbers or anything else.)

-
J('[] void')
+
J('[] void')
 
False
 
-
J('[[]] void')
+
J('[[]] void')
 
True
 
-
J('[[][[]]] void')
+
J('[[][[]]] void')
 
True
 
-
J('[[[]][[][]]] void')
+
J('[[[]][[][]]] void')
 
False
 
-
-
+
+
+
@@ -1387,7 +1333,7 @@ soley of containers, without strings or numbers or anything else.)


Thun Documentation by Simon Forman is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
Based on a work at https://osdn.net/projects/joypy/. - Created using Sphinx 1.7.3. + Created using Sphinx 4.3.0.
diff --git a/docs/sphinx_docs/_build/html/library.html b/docs/sphinx_docs/_build/html/library.html index 15936ce..4fdfc75 100644 --- a/docs/sphinx_docs/_build/html/library.html +++ b/docs/sphinx_docs/_build/html/library.html @@ -1,19 +1,18 @@ - + - + - - + + + Function Reference — Thun 0.4.1 documentation - - - - - - - + + + + + + @@ -30,90 +29,92 @@
+ +
-
+

Function Reference

-
+

joy.library

This module contains the Joy function infrastructure and a library of functions. Its main export is a Python function initialize() that returns a dictionary of Joy functions suitable for use with the joy() function.

-
-
-joy.library.BinaryBuiltinWrapper(f)[source]
+
+
+joy.library.BinaryBuiltinWrapper(f)[source]

Wrap functions that take two arguments and return a single result.

-
-
-class joy.library.DefinitionWrapper(name, body_text, doc=None)[source]
+
+
+class joy.library.DefinitionWrapper(name, body_text, doc=None)[source]

Provide implementation of defined functions, and some helper methods.

-
-
-classmethod add_def(definition, dictionary, fail_fails=False)[source]
+
+
+classmethod add_def(definition, dictionary, fail_fails=False)[source]

Add the definition to the dictionary.

-
-
-classmethod add_definitions(defs, dictionary)[source]
+
+
+classmethod add_definitions(defs, dictionary)[source]

Scan multi-line string defs for definitions and add them to the dictionary.

-
-
-classmethod parse_definition(defi)[source]
+
+
+classmethod parse_definition(defi)[source]

Given some text describing a Joy function definition parse it and return a DefinitionWrapper.

-
-
-joy.library.FunctionWrapper(f)[source]
+
+
+joy.library.FunctionWrapper(f)[source]

Set name attribute.

-
-
-joy.library.SimpleFunctionWrapper(f)[source]
+
+
+joy.library.SimpleFunctionWrapper(f)[source]

Wrap functions that take and return just a stack.

-
-
-joy.library.UnaryBuiltinWrapper(f)[source]
+
+
+joy.library.UnaryBuiltinWrapper(f)[source]

Wrap functions that take one argument and return a single result.

-
-
-joy.library.add_aliases(D, A)[source]
+
+
+joy.library.add_aliases(D, A)[source]

Given a dict and a iterable of (name, [alias, …]) pairs, create additional entries in the dict mapping each alias to the named function if it’s in the dict. Aliases for functions not in the dict are ignored.

-
-
-joy.library.app1(S, expression, dictionary)[source]
+
+
+joy.library.app1(S, expression, dictionary)[source]

Given a quoted program on TOS and anything as the second stack item run the program and replace the two args with the first result of the program.

-
         ... x [Q] . app1
+
     ... x [Q] . app1
 -----------------------------------
    ... [x ...] [Q] . infra first
 
-
-
-joy.library.app2(S, expression, dictionary)[source]
+
+
+joy.library.app2(S, expression, dictionary)[source]

Like app1 with two items.

       ... y x [Q] . app2
 -----------------------------------
@@ -123,9 +124,9 @@ program.

-
-
-joy.library.app3(S, expression, dictionary)[source]
+
+
+joy.library.app3(S, expression, dictionary)[source]

Like app1 with three items.

     ... z y x [Q] . app3
 -----------------------------------
@@ -136,9 +137,9 @@ program.

-
-
-joy.library.b(stack, expression, dictionary)[source]
+
+
+joy.library.b(stack, expression, dictionary)[source]
b == [i] dip i
 
 ... [P] [Q] b == ... [P] i [Q] i
@@ -147,27 +148,27 @@ program.

-
-
-joy.library.branch(stack, expression, dictionary)[source]
+
+
+joy.library.branch(stack, expression, dictionary)[source]

Use a Boolean value to select one of two quoted programs to run.

branch == roll< choice i
 
   False [F] [T] branch
 --------------------------
-          F
+      F
 
    True [F] [T] branch
 -------------------------
-             T
+         T
 
-
-
-joy.library.choice(stack, expression, dictionary)[source]
+
+
+joy.library.choice(stack)[source]

Use a Boolean value to select one of two items.

   A B False choice
 ----------------------
@@ -183,9 +184,9 @@ program.

Boolean value (so empty string, zero, etc. are counted as false, etc.)

-
-
-joy.library.clear(stack, expression, dictionary)[source]
+
+
+joy.library.clear(stack)[source]

Clear everything from the stack.

clear == stack [pop stack] loop
 
@@ -195,29 +196,29 @@ Boolean value (so empty string, zero, etc. are counted as false, etc.)

-
-
-joy.library.cmp_(stack, expression, dictionary)[source]
+
+
+joy.library.cmp_(stack, expression, dictionary)[source]

cmp takes two values and three quoted programs on the stack and runs one of the three depending on the results of comparing the two values:

-
   a b [G] [E] [L] cmp
+
   a b [G] [E] [L] cmp
 ------------------------- a > b
-        G
+    G
 
-   a b [G] [E] [L] cmp
+   a b [G] [E] [L] cmp
 ------------------------- a = b
-            E
+        E
 
-   a b [G] [E] [L] cmp
+   a b [G] [E] [L] cmp
 ------------------------- a < b
-                L
+        L
 
-
-
-joy.library.concat_(stack, expression, dictionary)[source]
+
+
+joy.library.concat_(S)[source]

Concatinate the two lists on the top of the stack.

   [a b c] [d e f] concat
 ----------------------------
@@ -226,9 +227,9 @@ one of the three depending on the results of comparing the two values:

-
-
-joy.library.cond(stack, expression, dictionary)[source]
+
+
+joy.library.cond(stack, expression, dictionary)[source]

This combinator works like a case statement. It expects a single quote on the stack that must contain zero or more condition quotes and a default quote. Each condition clause should contain a quoted predicate @@ -242,9 +243,9 @@ true. If no predicates return true the default function runs.

-
-
-joy.library.dip(stack, expression, dictionary)[source]
+
+
+joy.library.dip(stack, expression, dictionary)[source]

The dip combinator expects a quoted program on the stack and below it some item, it hoists the item into the expression and runs the program on the rest of the stack.

@@ -255,9 +256,9 @@ on the rest of the stack.

-
-
-joy.library.dipd(S, expression, dictionary)[source]
+
+
+joy.library.dipd(S, expression, dictionary)[source]

Like dip but expects two items.

   ... y x [Q] dip
 ---------------------
@@ -266,9 +267,9 @@ on the rest of the stack.

-
-
-joy.library.dipdd(S, expression, dictionary)[source]
+
+
+joy.library.dipdd(S, expression, dictionary)[source]

Like dip but expects three items.

   ... z y x [Q] dip
 -----------------------
@@ -277,23 +278,23 @@ on the rest of the stack.

-
-
-joy.library.disenstacken(stack, expression, dictionary)[source]
+
+
+joy.library.disenstacken(stack)[source]

The disenstacken operator expects a list on top of the stack and makes that the stack discarding the rest of the stack.

-
-
-joy.library.divmod_(stack, expression, dictionary)[source]
+
+
+joy.library.divmod_(S)[source]

divmod(x, y) -> (quotient, remainder)

Return the tuple (x//y, x%y). Invariant: div*y + mod == x.

-
-
-joy.library.drop(stack, expression, dictionary)[source]
+
+
+joy.library.drop(stack)[source]
drop == [rest] times
 
@@ -306,9 +307,9 @@ n items removed off the top.

-
-
-joy.library.dupdip(stack, expression, dictionary)[source]
+
+
+joy.library.dupdip(stack, expression, dictionary)[source]
[F] dupdip == dup [F] dip
 
 ... a [F] dupdip
@@ -319,18 +320,18 @@ n items removed off the top.

-
-
-joy.library.floor(x)[source]
-

Return the floor of x as a float. -This is the largest integral value <= x.

+
+
+joy.library.floor(n)[source]
+

Return the floor of x as an Integral.

+

This is the largest integer <= x.

-
-
-joy.library.genrec(stack, expression, dictionary)[source]
+
+
+joy.library.genrec(stack, expression, dictionary)[source]

General Recursion Combinator.

-
                      [if] [then] [rec1] [rec2] genrec
+
          [if] [then] [rec1] [rec2] genrec
 ---------------------------------------------------------------------
    [if] [then] [rec1 [[if] [then] [rec1] [rec2] genrec] rec2] ifte
 
@@ -373,9 +374,9 @@ the original definition in the else-part:

-
-
-joy.library.getitem(stack, expression, dictionary)[source]
+
+
+joy.library.getitem(stack)[source]
getitem == drop first
 
@@ -388,15 +389,15 @@ nth position in the quote counting from 0.

-
-
-joy.library.help_(S, expression, dictionary)[source]
+
+
+joy.library.help_(S, expression, dictionary)[source]

Accepts a quoted symbol on the top of the stack and prints its docs.

-
-
-joy.library.i(stack, expression, dictionary)[source]
+
+
+joy.library.i(stack, expression, dictionary)[source]

The i combinator expects a quoted program on the stack and unpacks it onto the pending expression for evaluation.

   [Q] i
@@ -406,15 +407,15 @@ onto the pending expression for evaluation.

-
-
-joy.library.id_(stack, expression, dictionary)[source]
+
+
+joy.library.id_(stack)[source]

The identity function.

-
-
-joy.library.infra(stack, expression, dictionary)[source]
+
+
+joy.library.infra(stack, expression, dictionary)[source]

Accept a quoted program and a list on the stack and run the program with the list as its stack. Does not affect the rest of the stack.

   ... [a b c] [Q] . infra
@@ -424,33 +425,34 @@ with the list as its stack.  Does not affect the rest of the stack.

-
-
-joy.library.initialize()[source]
+
+
+joy.library.initialize()[source]

Return a dictionary of Joy functions for use with joy().

-
-
-joy.library.inscribe(function)[source]
+
+
+joy.library.inscribe(function)[source]

A decorator to inscribe functions into the default dictionary.

-
-
-joy.library.inscribe_(stack, expression, dictionary)[source]
+
+
+joy.library.inscribe_(stack, expression, dictionary)[source]

Create a new Joy function definition in the Joy dictionary. A definition is given as a string with a name followed by a double equal sign then one or more Joy functions, the body. for example:

-
sqr == dup mul
+

sqr == dup mul

+

If you want the definition to persist over restarts, enter it into the definitions.txt resource.

-
-
-joy.library.loop(stack, expression, dictionary)[source]
+
+
+joy.library.loop(stack, expression, dictionary)[source]

Basic loop combinator.

   ... True [Q] loop
 -----------------------
@@ -458,39 +460,39 @@ the definitions.txt resource.

... False [Q] loop ------------------------ - ... + ...
-
-
-joy.library.map_(S, expression, dictionary)[source]
+
+
+joy.library.map_(S, expression, dictionary)[source]

Run the quoted program on TOS on the items in the list under it, push a new list with the results in place of the program and original list.

-
-
-joy.library.max_(stack, expression, dictionary)[source]
+
+
+joy.library.max_(S)[source]

Given a list find the maximum.

-
-
-joy.library.min_(stack, expression, dictionary)[source]
+
+
+joy.library.min_(S)[source]

Given a list find the minimum.

-
-
-joy.library.parse(stack, expression, dictionary)[source]
+
+
+joy.library.parse(stack)[source]

Parse the string on the stack to a Joy expression.

-
-
-joy.library.pm(stack, expression, dictionary)[source]
+
+
+joy.library.pm(stack)[source]

Plus or minus

   a b pm
 -------------
@@ -499,15 +501,15 @@ new list with the results in place of the program and original list.

-
-
-joy.library.pred(stack, expression, dictionary)[source]
+
+
+joy.library.pred(S)[source]

Decrement TOS.

-
-
-joy.library.primrec(stack, expression, dictionary)[source]
+
+
+joy.library.primrec(stack, expression, dictionary)[source]

From the “Overview of the language JOY”:

> The primrec combinator expects two quoted programs in addition to a data parameter. For an integer data parameter it works like this: If @@ -531,16 +533,16 @@ stack by the second element on the stack.:

------------------------------ Base - n [Base] [Recur] primrec + n [Base] [Recur] primrec ------------------------------------------ n > 0 n (n-1) [Base] [Recur] primrec Recur
-
-
-joy.library.remove(stack, expression, dictionary)[source]
+
+
+joy.library.remove(S)[source]

Expects an item on the stack and a quote under it and removes that item from the the quote. The item is only removed once.

   [1 2 3 1] 1 remove
@@ -550,18 +552,18 @@ from the the quote.  The item is only removed once.

-
-
-joy.library.reverse(stack, expression, dictionary)[source]
+
+
+joy.library.reverse(S)[source]

Reverse the list on the top of the stack.

reverse == [] swap shunt
 
-
-
-joy.library.select(stack, expression, dictionary)[source]
+
+
+joy.library.select(stack)[source]

Use a Boolean value to select one of two items from a sequence.

   [A B] False select
 ------------------------
@@ -578,15 +580,15 @@ Currently Python semantics are used to evaluate the “truthiness” of the
 Boolean value (so empty string, zero, etc. are counted as false, etc.)

-
-
-joy.library.sharing(stack, expression, dictionary)[source]
+
+
+joy.library.sharing(stack, expression, dictionary)[source]

Print redistribution information.

-
-
-joy.library.shunt(stack, expression, dictionary)[source]
+
+
+joy.library.shunt(stack)[source]

Like concat but reverses the top list into the second.

shunt == [swons] step == reverse swap concat
 
@@ -597,60 +599,60 @@ Boolean value (so empty string, zero, etc. are counted as false, etc.)

-
-
-joy.library.sort_(stack, expression, dictionary)[source]
+
+
+joy.library.sort_(S)[source]

Given a list return it sorted.

-
-
-joy.library.sqrt(a)[source]
+
+
+joy.library.sqrt(a)[source]

Return the square root of the number a. Negative numbers return complex roots.

-
-
-joy.library.step(S, expression, dictionary)[source]
+
+
+joy.library.step(S, expression, dictionary)[source]

Run a quoted program on each item in a sequence.

   ... [] [Q] . step
 -----------------------
-          ... .
+      ... .
 
 
    ... [a] [Q] . step
 ------------------------
-         ... a . Q
+     ... a . Q
 
 
    ... [a b c] [Q] . step
 ----------------------------------------
-             ... a . Q [b c] [Q] step
+         ... a . Q [b c] [Q] step
 

The step combinator executes the quotation on each member of the list on top of the stack.

-
-
-joy.library.succ(stack, expression, dictionary)[source]
+
+
+joy.library.succ(S)[source]

Increment TOS.

-
-
-joy.library.sum_(stack, expression, dictionary)[source]
+
+
+joy.library.sum_(S)[source]

Given a quoted sequence of numbers return the sum.

sum == 0 swap [+] step
 
-
-
-joy.library.take(stack, expression, dictionary)[source]
+
+
+joy.library.take(stack)[source]

Expects an integer and a quote on the stack and returns the quote with just the top n items in reverse order (because that’s easier and you can use reverse if needed.)

@@ -661,54 +663,54 @@ use reverse if needed.)

-
-
-joy.library.times(stack, expression, dictionary)[source]
+
+
+joy.library.times(stack, expression, dictionary)[source]

times == [– dip] cons [swap] infra [0 >] swap while pop

   ... n [Q] . times
 ---------------------  w/ n <= 0
-         ... .
+     ... .
 
 
    ... 1 [Q] . times
 -----------------------
-         ... . Q
+     ... . Q
 
 
    ... n [Q] . times
 -------------------------------------  w/ n > 1
-         ... . Q (n - 1) [Q] times
+     ... . Q (n - 1) [Q] times
 
-
-
-joy.library.unique(stack, expression, dictionary)[source]
+
+
+joy.library.unique(S)[source]

Given a list remove duplicate items.

-
-
-joy.library.void(stack, expression, dictionary)[source]
+
+
+joy.library.void(stack)[source]

True if the form on TOS is void otherwise False.

-
-
-joy.library.warranty(stack, expression, dictionary)[source]
+
+
+joy.library.warranty(stack, expression, dictionary)[source]

Print warranty information.

-
-
-joy.library.words(stack, expression, dictionary)[source]
+
+
+joy.library.words(stack, expression, dictionary)[source]

Print all the words in alphabetical order.

-
-
-joy.library.x(stack, expression, dictionary)[source]
+
+
+joy.library.x(stack, expression, dictionary)[source]
x == dup i
 
 ... [Q] x = ... [Q] dup i
@@ -718,281 +720,300 @@ use reverse if needed.)

-
-
-joy.library.zip_(stack, expression, dictionary)[source]
+
+
+joy.library.zip_(S)[source]

Replace the two lists on the top of the stack with a list of the pairs from each list. The smallest list sets the length of the result list.

- -
+ +

Auto-generated Functions

-
-
-joy.utils.generated_library.ccons(stack)[source]
+
+
+joy.utils.generated_library.ccons(stack)[source]
(a2 a1 [...1] -- [a2 a1 ...1])
 
-
-
-joy.utils.generated_library.cons(stack)[source]
+
+
+joy.utils.generated_library.cons(stack)[source]
(a1 [...0] -- [a1 ...0])
 
-
-
-joy.utils.generated_library.dup(stack)[source]
+
+
+joy.utils.generated_library.dup(stack)[source]
(a1 -- a1 a1)
 
-
-
-joy.utils.generated_library.dupd(stack)[source]
+
+
+joy.utils.generated_library.dupd(stack)[source]
(a2 a1 -- a2 a2 a1)
 
-
-
-joy.utils.generated_library.dupdd(stack)[source]
+
+
+joy.utils.generated_library.dupdd(stack)[source]
(a3 a2 a1 -- a3 a3 a2 a1)
 
-
-
-joy.utils.generated_library.first(stack)[source]
+
+
+joy.utils.generated_library.first(stack)[source]
([a1 ...1] -- a1)
 
-
-
-joy.utils.generated_library.first_two(stack)[source]
+
+
+joy.utils.generated_library.first_two(stack)[source]
([a1 a2 ...1] -- a1 a2)
 
-
-
-joy.utils.generated_library.fourth(stack)[source]
+
+
+joy.utils.generated_library.fourth(stack)[source]
([a1 a2 a3 a4 ...1] -- a4)
 
-
-
-joy.utils.generated_library.over(stack)[source]
+
+
+joy.utils.generated_library.over(stack)[source]
(a2 a1 -- a2 a1 a2)
 
-
-
-joy.utils.generated_library.pop(stack)[source]
+
+
+joy.utils.generated_library.pop(stack)[source]
(a1 --)
 
-
-
-joy.utils.generated_library.popd(stack)[source]
+
+
+joy.utils.generated_library.popd(stack)[source]
(a2 a1 -- a1)
 
-
-
-joy.utils.generated_library.popdd(stack)[source]
+
+
+joy.utils.generated_library.popdd(stack)[source]
(a3 a2 a1 -- a2 a1)
 
-
-
-joy.utils.generated_library.popop(stack)[source]
+
+
+joy.utils.generated_library.popop(stack)[source]
(a2 a1 --)
 
-
-
-joy.utils.generated_library.popopd(stack)[source]
+
+
+joy.utils.generated_library.popopd(stack)[source]
(a3 a2 a1 -- a1)
 
-
-
-joy.utils.generated_library.popopdd(stack)[source]
+
+
+joy.utils.generated_library.popopdd(stack)[source]
(a4 a3 a2 a1 -- a2 a1)
 
-
-
-joy.utils.generated_library.rest(stack)[source]
+
+
+joy.utils.generated_library.rest(stack)[source]
([a1 ...0] -- [...0])
 
-
-
-joy.utils.generated_library.rolldown(stack)[source]
+
+
+joy.utils.generated_library.rolldown(stack)[source]
(a1 a2 a3 -- a2 a3 a1)
 
-
-
-joy.utils.generated_library.rollup(stack)[source]
+
+
+joy.utils.generated_library.rollup(stack)[source]
(a1 a2 a3 -- a3 a1 a2)
 
-
-
-joy.utils.generated_library.rrest(stack)[source]
+
+
+joy.utils.generated_library.rrest(stack)[source]
([a1 a2 ...1] -- [...1])
 
-
-
-joy.utils.generated_library.second(stack)[source]
+
+
+joy.utils.generated_library.second(stack)[source]
([a1 a2 ...1] -- a2)
 
-
-
-joy.utils.generated_library.stack(stack)[source]
+
+
+joy.utils.generated_library.stack(stack)[source]
(... -- ... [...])
 
-
-
-joy.utils.generated_library.stuncons(stack)[source]
+
+
+joy.utils.generated_library.stuncons(stack)[source]
(... a1 -- ... a1 a1 [...])
 
-
-
-joy.utils.generated_library.stununcons(stack)[source]
+
+
+joy.utils.generated_library.stununcons(stack)[source]
(... a2 a1 -- ... a2 a1 a1 a2 [...])
 
-
-
-joy.utils.generated_library.swaack(stack)[source]
+
+
+joy.utils.generated_library.swaack(stack)[source]
([...1] -- [...0])
 
-
-
-joy.utils.generated_library.swap(stack)[source]
+
+
+joy.utils.generated_library.swap(stack)[source]
(a1 a2 -- a2 a1)
 
-
-
-joy.utils.generated_library.swons(stack)[source]
+
+
+joy.utils.generated_library.swons(stack)[source]
([...1] a1 -- [a1 ...1])
 
-
-
-joy.utils.generated_library.third(stack)[source]
+
+
+joy.utils.generated_library.third(stack)[source]
([a1 a2 a3 ...1] -- a3)
 
-
-
-joy.utils.generated_library.tuck(stack)[source]
+
+
+joy.utils.generated_library.tuck(stack)[source]
(a2 a1 -- a1 a2 a1)
 
-
-
-joy.utils.generated_library.uncons(stack)[source]
+
+
+joy.utils.generated_library.uncons(stack)[source]
([a1 ...0] -- a1 [...0])
 
-
-
-joy.utils.generated_library.unit(stack)[source]
+
+
+joy.utils.generated_library.unit(stack)[source]
(a1 -- [a1 ])
 
-
-
-joy.utils.generated_library.unswons(stack)[source]
+
+
+joy.utils.generated_library.unswons(stack)[source]
([a1 ...1] -- [...1] a1)
 
-
- + + +
@@ -1031,7 +1051,7 @@ from each list. The smallest list sets the length of the result list.


Thun Documentation by Simon Forman is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
Based on a work at https://osdn.net/projects/joypy/. - Created using Sphinx 1.7.3. + Created using Sphinx 4.3.0. diff --git a/docs/sphinx_docs/_build/html/notebooks/Categorical.html b/docs/sphinx_docs/_build/html/notebooks/Categorical.html index e288c87..420e11c 100644 --- a/docs/sphinx_docs/_build/html/notebooks/Categorical.html +++ b/docs/sphinx_docs/_build/html/notebooks/Categorical.html @@ -1,19 +1,18 @@ - + - + - - + + + Categorical Programming — Thun 0.4.1 documentation - - - - - - - + + + + + + @@ -30,25 +29,69 @@
+ +
-
+

Categorical Programming

DRAFT

Categorical

In Manfred von Thun’s article Joy compared with other functional languages he asks, “Could the language of categories be used for writing programs? Any lambda expression can be translated into a categorical expression, so the language of categories is expressively complete. But this does not make it a suitable language for writing programs. As it stands it is a very low-level language.”

In Compiling to categories Conal Elliott give a taste of what this might mean.

-
It is well-known that the simply typed lambda-calculus is modeled by any cartesian closed category (CCC). This correspondence suggests giving typed functional programs a variety of interpretations, each corresponding to a different category. A convenient way to realize this idea is as a collection of meaning-preserving transformations added to an existing compiler, such as GHC for Haskell. This paper describes such an implementation and demonstrates its use for a variety of interpretations including hardware circuits, automatic differentiation, incremental computation, and interval analysis. Each such interpretation is a category easily defined in Haskell (outside of the compiler). The general technique appears to provide a compelling alternative to deeply embedded domain-specific languages.
+

It is well-known that the simply typed lambda-calculus is modeled by any cartesian closed category (CCC). This correspondence suggests giving typed functional programs a variety of interpretations, each corresponding to a different category. A convenient way to realize this idea is as a collection of meaning-preserving transformations added to an existing compiler, such as GHC for Haskell. This paper describes such an implementation and demonstrates its use for a variety of interpretations including hardware circuits, automatic differentiation, incremental computation, and interval analysis. Each such interpretation is a category easily defined in Haskell (outside of the compiler). The general technique appears to provide a compelling alternative to deeply embedded domain-specific languages.

+

What he’s doing is translating lambda forms into a kind of “point-free” style that is very close to Joy code (although more verbose) and then showing how to instantiate that code over different categories to get several different kinds of program out of the same code.

-
+
+
@@ -921,7 +927,7 @@ derivative-with-respect-to-N of some other state/RE:


Thun Documentation by Simon Forman is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
Based on a work at https://osdn.net/projects/joypy/. - Created using Sphinx 1.7.3. + Created using Sphinx 4.3.0. diff --git a/docs/sphinx_docs/_build/html/notebooks/Developing.html b/docs/sphinx_docs/_build/html/notebooks/Developing.html index 92d3588..b670664 100644 --- a/docs/sphinx_docs/_build/html/notebooks/Developing.html +++ b/docs/sphinx_docs/_build/html/notebooks/Developing.html @@ -1,19 +1,19 @@ - + - + - - + + + Developing a Program in Joy — Thun 0.4.1 documentation - - - - - - - + + + + + + + @@ -30,28 +30,30 @@
+ +
-
+

Developing a Program in Joy

As an example of developing a program in Joy let’s take the first problem from the Project Euler website.

-
+

Project Euler, first problem: “Multiples of 3 and 5”

If we list all the natural numbers below 10 that are multiples of 3 or 5, we get 3, 5, 6 and 9. The sum of these multiples is 23.

Find the sum of all the multiples of 3 or 5 below 1000.

-
from notebook_preamble import J, V, define
+
from notebook_preamble import J, V, define
 
-
+

Sum a range filtered by a predicate

Let’s create a predicate that returns True if a number is a multiple of 3 or 5 and False otherwise.

-
define('P == [3 % not] dupdip 5 % not or')
+
define('P == [3 % not] dupdip 5 % not or')
 
-
V('80 P')
+
V('80 P')
 
             . 80 P
@@ -80,8 +82,8 @@ should be; we generate one thousand integers but actually use less than
 half of them. A better solution would be to generate just the multiples
 we want to sum, and to add them as we go rather than storing them and
 and summing them at the end.

-
-
+
+

Generate just the multiples

At first I had the idea to use two counters and increase them by three and five, respectively. This way we only generate the terms that we @@ -114,10 +116,10 @@ the counter to the running sum. This function will do that:

PE1.1 == + [+] dupdip
 
-
define('PE1.1 == + [+] dupdip')
+
define('PE1.1 == + [+] dupdip')
 
-
V('0 0 3 PE1.1')
+
V('0 0 3 PE1.1')
 
        . 0 0 3 PE1.1
@@ -132,7 +134,7 @@ the counter to the running sum. This function will do that:

3 3 .
-
V('0 0 [3 2 1 3 1 2 3] [PE1.1] step')
+
V('0 0 [3 2 1 3 1 2 3] [PE1.1] step')
 
                            . 0 0 [3 2 1 3 1 2 3] [PE1.1] step
@@ -212,29 +214,29 @@ the counter to the running sum. This function will do that:

So one step through all seven terms brings the counter to 15 and the total to 60.

-
-
+
+

How many multiples to sum?

-
1000 / 15
+
1000 / 15
 
66
 
-
66 * 15
+
66 * 15
 
990
 
-
1000 - 990
+
1000 - 990
 
10
 

We only want the terms less than 1000.

-
999 - 990
+
999 - 990
 
9
@@ -242,17 +244,17 @@ total to 60.

That means we want to run the full list of numbers sixty-six times to get to 990 and then the first four numbers 3 2 1 3 to get to 999.

-
define('PE1 == 0 0 66 [[3 2 1 3 1 2 3] [PE1.1] step] times [3 2 1 3] [PE1.1] step pop')
+
define('PE1 == 0 0 66 [[3 2 1 3 1 2 3] [PE1.1] step] times [3 2 1 3] [PE1.1] step pop')
 
-
J('PE1')
+
J('PE1')
 
233168
 
-
-
+
+

Packing the terms into an integer

This form uses no extra storage and produces no unused summands. It’s good but there’s one more trick we can apply. The list of seven terms @@ -265,16 +267,16 @@ integer terms from the list.

0b 11 10 01 11 01 10 11 == 14811
-
0b11100111011011
+
0b11100111011011
 
14811
 
-
define('PE1.2 == [3 & PE1.1] dupdip 2 >>')
+
define('PE1.2 == [3 & PE1.1] dupdip 2 >>')
 
-
V('0 0 14811 PE1.2')
+
V('0 0 14811 PE1.2')
 
                      . 0 0 14811 PE1.2
@@ -297,7 +299,7 @@ integer terms from the list.

3 3 3702 .
-
V('3 3 3702 PE1.2')
+
V('3 3 3702 PE1.2')
 
                     . 3 3 3702 PE1.2
@@ -320,7 +322,7 @@ integer terms from the list.

8 5 925 .
-
V('0 0 14811 7 [PE1.2] times pop')
+
V('0 0 14811 7 [PE1.2] times pop')
 
                      . 0 0 14811 7 [PE1.2] times pop
@@ -457,17 +459,17 @@ integer terms from the list.

And so we have at last:

-
define('PE1 == 0 0 66 [14811 7 [PE1.2] times pop] times 14811 4 [PE1.2] times popop')
+
define('PE1 == 0 0 66 [14811 7 [PE1.2] times pop] times 14811 4 [PE1.2] times popop')
 
-
J('PE1')
+
J('PE1')
 
233168
 
-
-
+
+

Let’s refactor

  14811 7 [PE1.2] times pop
   14811 4 [PE1.2] times pop
@@ -475,14 +477,14 @@ integer terms from the list.

n 14811 swap [PE1.2] times pop
-
define('PE1.3 == 14811 swap [PE1.2] times pop')
+
define('PE1.3 == 14811 swap [PE1.2] times pop')
 

Now we can simplify the definition above:

-
define('PE1 == 0 0 66 [7 PE1.3] times 4 PE1.3 pop')
+
define('PE1 == 0 0 66 [7 PE1.3] times 4 PE1.3 pop')
 
-
J('PE1')
+
J('PE1')
 
233168
@@ -497,18 +499,18 @@ I hope it’s clear.

PE1 == 0 0 66 [7 PE1.3] times 4 PE1.3 pop
-
-
-
+
+
+

Generator Version

It’s a little clunky iterating sixty-six times though the seven numbers then four more. In the Generator Programs notebook we derive a generator that can be repeatedly driven by the x combinator to produce a stream of the seven numbers repeating over and over again.

-
define('PE1.terms == [0 swap [dup [pop 14811] [] branch [3 &] dupdip 2 >>] dip rest cons]')
+
define('PE1.terms == [0 swap [dup [pop 14811] [] branch [3 &] dupdip 2 >>] dip rest cons]')
 
-
J('PE1.terms 21 [x] times')
+
J('PE1.terms 21 [x] times')
 
3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 [0 swap [dup [pop 14811] [] branch [3 &] dupdip 2 >>] dip rest cons]
@@ -516,19 +518,19 @@ produce a stream of the seven numbers repeating over and over again.

We know from above that we need sixty-six times seven then four more terms to reach up to but not over one thousand.

-
J('7 66 * 4 +')
+
J('7 66 * 4 +')
 
466
 
-
J('PE1.terms 466 [x] times pop')
+
J('PE1.terms 466 [x] times pop')
 
3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3
 
-
J('[PE1.terms 466 [x] times pop] run sum')
+
J('[PE1.terms 466 [x] times pop] run sum')
 
999
@@ -537,18 +539,18 @@ terms to reach up to but not over one thousand.

Now we can use PE1.1 to accumulate the terms as we go, and then pop the generator and the counter from the stack when we’re done, leaving just the sum.

-
J('0 0 PE1.terms 466 [x [PE1.1] dip] times popop')
+
J('0 0 PE1.terms 466 [x [PE1.1] dip] times popop')
 
233168
 
-
-
+
+

A little further analysis renders iteration unnecessary.

Consider finding the sum of the positive integers less than or equal to ten.

-
J('[10 9 8 7 6 5 4 3 2 1] sum')
+
J('[10 9 8 7 6 5 4 3 2 1] sum')
 
55
@@ -571,10 +573,10 @@ positive integers is:

(The formula also works for odd values of N, I’ll leave that to you if you want to work it out or you can take my word for it.)

-
define('F == dup ++ * 2 floordiv')
+
define('F == dup ++ * 2 floordiv')
 
-
V('10 F')
+
V('10 F')
 
      . 10 F
@@ -598,13 +600,13 @@ each, starting with:

If we reverse one of these two blocks and sum pairs…

-
J('[3 5 6 9 10 12 15] reverse [978 980 981 984 985 987 990] zip')
+
J('[3 5 6 9 10 12 15] reverse [978 980 981 984 985 987 990] zip')
 
[[978 15] [980 12] [981 10] [984 9] [985 6] [987 5] [990 3]]
 
-
J('[3 5 6 9 10 12 15] reverse [978 980 981 984 985 987 990] zip [sum] map')
+
J('[3 5 6 9 10 12 15] reverse [978 980 981 984 985 987 990] zip [sum] map')
 
[993 992 991 993 991 992 993]
@@ -612,7 +614,7 @@ each, starting with:

(Interesting that the sequence of seven numbers appears again in the rightmost digit of each term.)

-
J('[ 3 5 6 9 10 12 15] reverse [978 980 981 984 985 987 990] zip [sum] map sum')
+
J('[ 3 5 6 9 10 12 15] reverse [978 980 981 984 985 987 990] zip [sum] map sum')
 
6945
@@ -626,7 +628,7 @@ additional unpaired terms between 990 and 1000:

So we can give the “sum of all the multiples of 3 or 5 below 1000” like so:

-
J('6945 33 * [993 995 996 999] cons sum')
+
J('6945 33 * [993 995 996 999] cons sum')
 
233168
@@ -648,8 +650,8 @@ directly from the diagram: 4 3 1 4 2 2 4 1 3 4.

lcm don’t matter, the pattern they make will always be symmetrical around its midpoint. The same reasoning holds for multiples of more than two numbers.

-
-
+
+

The Simplest Program

Of course, the simplest joy program for the first Project Euler problem is just:

@@ -657,32 +659,55 @@ is just:

Fin.

-
-
+ +
+
@@ -723,7 +747,7 @@ is just:


Thun Documentation by Simon Forman is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
Based on a work at https://osdn.net/projects/joypy/. - Created using Sphinx 1.7.3. + Created using Sphinx 4.3.0. diff --git a/docs/sphinx_docs/_build/html/notebooks/Generator_Programs.html b/docs/sphinx_docs/_build/html/notebooks/Generator_Programs.html index 6587f87..fe9f8b9 100644 --- a/docs/sphinx_docs/_build/html/notebooks/Generator_Programs.html +++ b/docs/sphinx_docs/_build/html/notebooks/Generator_Programs.html @@ -1,19 +1,18 @@ - + - + - - + + + Using x to Generate Values — Thun 0.4.1 documentation - - - - - - - + + + + + + @@ -30,12 +29,14 @@
+ +
-
+

Using x to Generate Values

Cf. jp-reprod.html

-
from notebook_preamble import J, V, define
+
from notebook_preamble import J, V, define
 

Consider the x combinator:

@@ -75,7 +76,7 @@ function C

Let’s try it:

-
V('[0 swap [dup ++] dip rest cons] x')
+
V('[0 swap [dup ++] dip rest cons] x')
 
                                           . [0 swap [dup ++] dip rest cons] x
@@ -94,18 +95,18 @@ function C
 

After one application of x the quoted program contains 1 and 0 is below it on the stack.

-
J('[0 swap [dup ++] dip rest cons] x x x x x pop')
+
J('[0 swap [dup ++] dip rest cons] x x x x x pop')
 
0 1 2 3 4
 
-
+

direco

-
define('direco == dip rest cons')
+
define('direco == dip rest cons')
 
-
V('[0 swap [dup ++] direco] x')
+
V('[0 swap [dup ++] direco] x')
 
                                    . [0 swap [dup ++] direco] x
@@ -123,8 +124,8 @@ function C0 [1 swap [dup ++] direco] .
 
-
-
+
+

Making Generators

We want to define a function that accepts a and [C] and builds our quoted program:

@@ -146,44 +147,44 @@ our quoted program:

G == [direco] cons [swap] swoncat cons
-
define('G == [direco] cons [swap] swoncat cons')
+
define('G == [direco] cons [swap] swoncat cons')
 

Let’s try it out:

-
J('0 [dup ++] G')
+
J('0 [dup ++] G')
 
[0 swap [dup ++] direco]
 
-
J('0 [dup ++] G x x x pop')
+
J('0 [dup ++] G x x x pop')
 
0 1 2
 
-
+

Powers of 2

-
J('1 [dup 1 <<] G x x x x x x x x x pop')
+
J('1 [dup 1 <<] G x x x x x x x x x pop')
 
1 2 4 8 16 32 64 128 256
 
-
-
+
+

[x] times

If we have one of these quoted programs we can drive it using times with the x combinator.

-
J('23 [dup ++] G 5 [x] times')
+
J('23 [dup ++] G 5 [x] times')
 
23 24 25 26 27 [28 swap [dup ++] direco]
 
-
-
-
+
+
+

Generating Multiples of Three and Five

Look at the treatment of the Project Euler Problem One in the “Developing a Program” notebook and you’ll see that we might be @@ -199,10 +200,10 @@ int:

And pick them off by masking with 3 (binary 11) and then shifting the int right two bits.

-
define('PE1.1 == dup [3 &] dip 2 >>')
+
define('PE1.1 == dup [3 &] dip 2 >>')
 
-
V('14811 PE1.1')
+
V('14811 PE1.1')
 
                  . 14811 PE1.1
@@ -219,33 +220,33 @@ int right two bits.

If we plug 14811 and [PE1.1] into our generator form…

-
J('14811 [PE1.1] G')
+
J('14811 [PE1.1] G')
 
[14811 swap [PE1.1] direco]
 

…we get a generator that works for seven cycles before it reaches zero:

-
J('[14811 swap [PE1.1] direco] 7 [x] times')
+
J('[14811 swap [PE1.1] direco] 7 [x] times')
 
3 2 1 3 1 2 3 [0 swap [PE1.1] direco]
 
-
+

Reset at Zero

We need a function that checks if the int has reached zero and resets it if so.

-
define('PE1.1.check == dup [pop 14811] [] branch')
+
define('PE1.1.check == dup [pop 14811] [] branch')
 
-
J('14811 [PE1.1.check PE1.1] G')
+
J('14811 [PE1.1.check PE1.1] G')
 
[14811 swap [PE1.1.check PE1.1] direco]
 
-
J('[14811 swap [PE1.1.check PE1.1] direco] 21 [x] times')
+
J('[14811 swap [PE1.1.check PE1.1] direco] 21 [x] times')
 
3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 [0 swap [PE1.1.check PE1.1] direco]
@@ -255,47 +256,47 @@ if so.

that’s a little beyond the scope of this article. This solution does extra work, but not much, and we’re not using it “in production” as they say.)

-
-
+
+

Run 466 times

In the PE1 problem we are asked to sum all the multiples of three and five less than 1000. It’s worked out that we need to use all seven numbers sixty-six times and then four more.

-
J('7 66 * 4 +')
+
J('7 66 * 4 +')
 
466
 

If we drive our generator 466 times and sum the stack we get 999.

-
J('[14811 swap [PE1.1.check PE1.1] direco] 466 [x] times')
+
J('[14811 swap [PE1.1.check PE1.1] direco] 466 [x] times')
 
3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 [57 swap [PE1.1.check PE1.1] direco]
 
-
J('[14811 swap [PE1.1.check PE1.1] direco] 466 [x] times pop enstacken sum')
+
J('[14811 swap [PE1.1.check PE1.1] direco] 466 [x] times pop enstacken sum')
 
999
 
-
-
-
+
+ +

Project Euler Problem One

-
define('PE1.2 == + dup [+] dip')
+
define('PE1.2 == + dup [+] dip')
 

Now we can add PE1.2 to the quoted program given to G.

-
J('0 0 0 [PE1.1.check PE1.1] G 466 [x [PE1.2] dip] times popop')
+
J('0 0 0 [PE1.1.check PE1.1] G 466 [x [PE1.2] dip] times popop')
 
233168
 
-
-
+
+

A generator for the Fibonacci Sequence.

Consider:

[b a F] x
@@ -350,42 +351,43 @@ numbers sixty-six times and then four more.

fib_gen == [1 1 F]
-
define('fib == + [popdd over] cons infra uncons')
+
define('fib == + [popdd over] cons infra uncons')
 
-
define('fib_gen == [1 1 fib]')
+
define('fib_gen == [1 1 fib]')
 
-
J('fib_gen 10 [x] times')
+
J('fib_gen 10 [x] times')
 
1 2 3 5 8 13 21 34 55 89 [144 89 fib]
 
-
-
+
+

Project Euler Problem Two

-
By considering the terms in the Fibonacci sequence whose values do -not exceed four million, find the sum of the even-valued terms.
+

By considering the terms in the Fibonacci sequence whose values do +not exceed four million, find the sum of the even-valued terms.

+

Now that we have a generator for the Fibonacci sequence, we need a function that adds a term in the sequence to a sum if it is even, and pops it otherwise.

-
define('PE2.1 == dup 2 % [+] [pop] branch')
+
define('PE2.1 == dup 2 % [+] [pop] branch')
 

And a predicate function that detects when the terms in the series “exceed four million”.

-
define('>4M == 4000000 >')
+
define('>4M == 4000000 >')
 

Now it’s straightforward to define PE2 as a recursive function that generates terms in the Fibonacci sequence until they exceed four million and sums the even ones.

-
define('PE2 == 0 fib_gen x [pop >4M] [popop] [[PE2.1] dip x] primrec')
+
define('PE2 == 0 fib_gen x [pop >4M] [popop] [[PE2.1] dip x] primrec')
 
-
J('PE2')
+
J('PE2')
 
4613732
@@ -402,7 +404,7 @@ and sums the even ones.

PE2 == 0 fib_gen x [pop >4M] [popop] [[PE2.1] dip x] primrec
-
+

Even-valued Fibonacci Terms

Using o for odd and e for even:

o + o = e
@@ -416,23 +418,23 @@ and sums the even ones.

Every third term is even.

-
J('[1 0 fib] x x x')  # To start the sequence with 1 1 2 3 instead of 1 2 3.
+
J('[1 0 fib] x x x')  # To start the sequence with 1 1 2 3 instead of 1 2 3.
 
1 1 2 [3 2 fib]
 

Drive the generator three times and popop the two odd terms.

-
J('[1 0 fib] x x x [popop] dipd')
+
J('[1 0 fib] x x x [popop] dipd')
 
2 [3 2 fib]
 
-
define('PE2.2 == x x x [popop] dipd')
+
define('PE2.2 == x x x [popop] dipd')
 
-
J('[1 0 fib] 10 [PE2.2] times')
+
J('[1 0 fib] 10 [PE2.2] times')
 
2 8 34 144 610 2584 10946 46368 196418 832040 [1346269 832040 fib]
@@ -440,25 +442,25 @@ and sums the even ones.

Replace x with our new driver function PE2.2 and start our fib generator at 1 0.

-
J('0 [1 0 fib] PE2.2 [pop >4M] [popop] [[PE2.1] dip PE2.2] primrec')
+
J('0 [1 0 fib] PE2.2 [pop >4M] [popop] [[PE2.1] dip PE2.2] primrec')
 
4613732
 
-
-
-
+
+
+

How to compile these?

You would probably start with a special version of G, and perhaps modifications to the default x?

-
-
+ +

An Interesting Variation

-
define('codireco == cons dip rest cons')
+
define('codireco == cons dip rest cons')
 
-
V('[0 [dup ++] codireco] x')
+
V('[0 [dup ++] codireco] x')
 
                                 . [0 [dup ++] codireco] x
@@ -477,49 +479,64 @@ modifications to the default 0 [1 [dup ++] codireco] .
 
-
define('G == [codireco] cons cons')
+
define('G == [codireco] cons cons')
 
-
J('230 [dup ++] G 5 [x] times pop')
+
J('230 [dup ++] G 5 [x] times pop')
 
230 231 232 233 234
 
-
-
+
+
+
@@ -560,7 +576,7 @@ modifications to the default
Thun Documentation by Simon Forman is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
Based on a work at https://osdn.net/projects/joypy/. - Created using Sphinx 1.7.3. + Created using Sphinx 4.3.0.
diff --git a/docs/sphinx_docs/_build/html/notebooks/Intro.html b/docs/sphinx_docs/_build/html/notebooks/Intro.html index c0c812d..bf30d3b 100644 --- a/docs/sphinx_docs/_build/html/notebooks/Intro.html +++ b/docs/sphinx_docs/_build/html/notebooks/Intro.html @@ -1,19 +1,18 @@ - + - + - - + + + Thun: Joy in Python — Thun 0.4.1 documentation - - - - - - - + + + + + + @@ -30,20 +29,22 @@
+ +
-
+

Thun: Joy in Python

This implementation is meant as a tool for exploring the programming model and method of Joy. Python seems like a great implementation language for Joy for several reasons.

    -
  • We can lean on the Python immutable types for our basic semantics and types: ints, floats, strings, and tuples, which enforces functional purity.
  • -
  • We get garbage collection for free.
  • -
  • Compilation via Cython.
  • -
  • Python is a “glue language” with loads of libraries which we can wrap in Joy functions.
  • +
  • We can lean on the Python immutable types for our basic semantics and types: ints, floats, strings, and tuples, which enforces functional purity.

  • +
  • We get garbage collection for free.

  • +
  • Compilation via Cython.

  • +
  • Python is a “glue language” with loads of libraries which we can wrap in Joy functions.

-
+

Read-Eval-Print Loop (REPL)

The main way to interact with the Joy interpreter is through a simple REPL @@ -86,8 +87,8 @@ joy? joy?

-
-
+ +

The Stack

In Joy, in addition to the types Boolean, integer, float, and string, there is a single sequence type represented by enclosing a sequence of @@ -95,21 +96,21 @@ terms in brackets [ both the stack and the expression. It is a cons list made from Python tuples.

-
-
+ +

Purely Functional Datastructures

Because Joy stacks are made out of Python tuples they are immutable, as are the other Python types we “borrow” for Joy, so all Joy datastructures are purely functional.

-
-
+ +

The joy() function

-
+

An Interpreter

The joy() interpreter function is extrememly simple. It accepts a stack, an expression, and a dictionary, and it iterates through the expression putting values onto the stack and delegating execution to functions which it looks up in the dictionary.

-
-
+
+

Continuation-Passing Style

One day I thought, What happens if you rewrite Joy to use CPS? I @@ -117,8 +118,8 @@ made all the functions accept and return the expression as well as the stack and found that all the combinators could be rewritten to work by modifying the expression rather than making recursive calls to the joy() function.

-
-
+ +

View function

The joy() function accepts an optional viewer argument that is a function which it calls on @@ -127,70 +128,70 @@ evaluation. This can be used for tracing, breakpoints, retrying after exceptions, or interrupting an evaluation and saving to disk or sending over the network to resume later. The stack and expression together contain all the state of the computation at each step.

-
-
+ +

The TracePrinter.

A viewer records each step of the evaluation of a Joy program. The TracePrinter has a facility for printing out a trace of the evaluation, one line per step. Each step is aligned to the current interpreter position, signified by a period separating the stack on the left from the pending expression (“continuation”) on the right.

-
-
-
+ + +

Parser

The parser is extremely simple. The undocumented re.Scanner class does the tokenizing and then the parser builds the tuple structure out of the tokens. There’s no Abstract Syntax Tree or anything like that.

-
+

Symbols

TODO: Symbols are just a string subclass; used by the parser to represent function names and by the interpreter to look up functions in the dictionary. N.B.: Symbols are not looked up at parse-time. You could define recursive functions, er, recusively, without genrec or other recursion combinators foo == ... foo ... but don’t do that.

-
-
+
+

Token Regular Expressions

123   1.2   'single quotes'  "double quotes"   function
 

TBD (look in the :module: joy.parser module.)

-
-
+ +

Examples

-
joy.parser.text_to_expression('1 2 3 4 5')  # A simple sequence.
+
joy.parser.text_to_expression('1 2 3 4 5')  # A simple sequence.
 
(1, (2, (3, (4, (5, ())))))
 
-
joy.parser.text_to_expression('[1 2 3] 4 5')  # Three items, the first is a list with three items
+
joy.parser.text_to_expression('[1 2 3] 4 5')  # Three items, the first is a list with three items
 
((1, (2, (3, ()))), (4, (5, ())))
 
-
joy.parser.text_to_expression('1 23 ["four" [-5.0] cons] 8888')  # A mixed bag. cons is
-                                                                 # a Symbol, no lookup at
-                                                                 # parse-time.  Haiku docs.
+
joy.parser.text_to_expression('1 23 ["four" [-5.0] cons] 8888')  # A mixed bag. cons is
+                                                                 # a Symbol, no lookup at
+                                                                 # parse-time.  Haiku docs.
 
(1, (23, (('four', ((-5.0, ()), (cons, ()))), (8888, ()))))
 
-
joy.parser.text_to_expression('[][][][][]')  # Five empty lists.
+
joy.parser.text_to_expression('[][][][][]')  # Five empty lists.
 
((), ((), ((), ((), ((), ())))))
 
-
joy.parser.text_to_expression('[[[[[]]]]]')  # Five nested lists.
+
joy.parser.text_to_expression('[[[[[]]]]]')  # Five nested lists.
 
((((((), ()), ()), ()), ()), ())
 
-
-
-
+
+ +

Library

The Joy library of functions (aka commands, or “words” after Forth usage) encapsulates all the actual functionality (no pun intended) of @@ -198,7 +199,7 @@ the Joy system. There are simple functions such as addition +, the library module supports aliases), and combinators which provide control-flow and higher-order operations.

Many of the functions are defined in Python, like dip:

-
print inspect.getsource(joy.library.dip)
+
print inspect.getsource(joy.library.dip)
 
def dip(stack, expression, dictionary):
@@ -211,11 +212,10 @@ provide control-flow and higher-order operations.

When the interpreter executes a definition function that function just pushes its body expression onto the pending expression (the continuation) and returns control to the interpreter.

-
print joy.library.definitions
+
print joy.library.definitions
 
-
-second == rest first
+
second == rest first
 third == rest rest first
 product == 1 swap [*] step
 swons == swap cons
@@ -248,8 +248,7 @@ anamorphism == [pop []] swap [dip swons] genrec
 range == [0 <=] [1 - dup] anamorphism
 while == swap [nullary] cons dup dipd concat loop
 dudipd == dup dipd
-primrec == [i] genrec
-
+primrec == [i] genrec

Currently, there’s no function to add new definitions to the dictionary from “within” Joy code itself. Adding new definitions remains a meta-interpreter action. You have to do it yourself, in Python, and wash @@ -260,7 +259,7 @@ stack and expression. There’s an implicit standard dictionary that defines the actual semantics of the syntactic stack and expression datastructures (which only contain symbols, not the actual functions. Pickle some and see for yourself.)

-
+

“There should be only one.”

Which brings me to talking about one of my hopes and dreams for this notation: “There should be only one.” What I mean is that there should @@ -277,8 +276,8 @@ frameworks, programming languages. It’s a waste of time, a +

+

Literary Code Library

If you read over the other notebooks you’ll see that developing code in Joy is a lot like doing simple mathematics, and the descriptions of the @@ -292,43 +291,47 @@ card is highly desirable. Less code has fewer errors. The structure of Joy engenders a kind of thinking that seems to be very effective for developing structured processes.

There seems to be an elegance and power to the notation.

-
-
-
+
+ +
+
@@ -367,7 +369,7 @@ developing structured processes.


Thun Documentation by Simon Forman is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
Based on a work at https://osdn.net/projects/joypy/. - Created using Sphinx 1.7.3. + Created using Sphinx 4.3.0.
diff --git a/docs/sphinx_docs/_build/html/notebooks/Newton-Raphson.html b/docs/sphinx_docs/_build/html/notebooks/Newton-Raphson.html index 2999601..0414a47 100644 --- a/docs/sphinx_docs/_build/html/notebooks/Newton-Raphson.html +++ b/docs/sphinx_docs/_build/html/notebooks/Newton-Raphson.html @@ -1,19 +1,19 @@ - + - + - - + + + Newton’s method — Thun 0.4.1 documentation - - - - - - - + + + + + + + @@ -30,18 +30,20 @@
+ +
-
+

Newton’s method

Let’s use the Newton-Raphson method for finding the root of an equation to write a function that can compute the square root of a number.

Cf. “Why Functional Programming Matters” by John Hughes

-
from notebook_preamble import J, V, define
+
from notebook_preamble import J, V, define
 
-
+

A Generator for Approximations

To make a generator that generates successive approximations let’s start by assuming an initial approximation and then derive the function that @@ -51,7 +53,7 @@ computes the next approximation:

a'
-
+

A Function to Compute the Next Approximation

This is the equation for computing the next approximate value of the square root:

@@ -67,8 +69,8 @@ square root:

F == n over / + 2 /
 
-
-
+
+

Make it into a Generator

Our generator would be created by:

a [dup F] make_generator
@@ -90,10 +92,10 @@ function we’re writing. If we let 1 be the initial approximation:

1 [dup 23 over / + 2 /] make_generator
-
define('gsra 1 swap [over / + 2 /] cons [dup] swoncat make_generator')
+
define('gsra 1 swap [over / + 2 /] cons [dup] swoncat make_generator')
 
-
J('23 gsra')
+
J('23 gsra')
 
[1 [dup 23 over / + 2 /] codireco]
@@ -101,23 +103,24 @@ function we’re writing. If we let 1 be the initial approximation:

Let’s drive the generator a few time (with the x combinator) and square the approximation to see how well it works…

-
J('23 gsra 6 [x popd] times first sqr')
+
J('23 gsra 6 [x popd] times first sqr')
 
23.0000000001585
 
-
-
-
+
+ +

Finding Consecutive Approximations within a Tolerance

From “Why Functional Programming Matters” by John Hughes:

-
The remainder of a square root finder is a function within, which +

The remainder of a square root finder is a function within, which takes a tolerance and a list of approximations and looks down the list for two successive approximations that differ by no more than -the given tolerance.

+the given tolerance.

+

(And note that by “list” he means a lazily-evaluated list.)

Using the output [a G] of the above generator for square root approximations, and further assuming that the first term a has been @@ -132,7 +135,7 @@ generated already and epsilon ε is handy on the stack…

b [c G] ε within
-
+

Predicate

a [b G]             ε [first - abs] dip <=
 a [b G] first - abs ε                   <=
@@ -142,11 +145,11 @@ generated already and epsilon ε is handy on the stack…

(abs(a-b)<=ε)
-
define('_within_P [first - abs] dip <=')
+
define('_within_P [first - abs] dip <=')
 
-
-
+
+

Base-Case

a [b G] ε roll< popop first
   [b G] ε a     popop first
@@ -154,19 +157,19 @@ generated already and epsilon ε is handy on the stack…

b
-
define('_within_B roll< popop first')
+
define('_within_B roll< popop first')
 
-
-
+
+

Recur

a [b G] ε R0 [within] R1
 
    -
  1. Discard a.
  2. -
  3. Use x combinator to generate next term from G.
  4. -
  5. Run within with i (it is a “tail-recursive” function.)
  6. +
  7. Discard a.

  8. +
  9. Use x combinator to generate next term from G.

  10. +
  11. Run within with i (it is a “tail-recursive” function.)

Pretty straightforward:

a [b G]        ε R0           [within] R1
@@ -179,11 +182,11 @@ generated already and epsilon ε is handy on the stack…

b [c G] ε within
-
define('_within_R [popd x] dip')
+
define('_within_R [popd x] dip')
 
-
-
+
+

Setting up

The recursive function we have defined so far needs a slight preamble: x to prime the generator and the epsilon value to use:

@@ -191,66 +194,88 @@ generated already and epsilon ε is handy on the stack…

a [b G] ε ...
-
define('within x 0.000000001 [_within_P] [_within_B] [_within_R] tailrec')
-define('sqrt gsra within')
+
define('within x 0.000000001 [_within_P] [_within_B] [_within_R] tailrec')
+define('sqrt gsra within')
 

Try it out…

-
J('36 sqrt')
+
J('36 sqrt')
 
6.0
 
-
J('23 sqrt')
+
J('23 sqrt')
 
4.795831523312719
 

Check it.

-
4.795831523312719**2
+
4.795831523312719**2
 
22.999999999999996
 
-
from math import sqrt
+
from math import sqrt
 
-sqrt(23)
+sqrt(23)
 
4.795831523312719
 
-
-
-
+ + +
+
@@ -291,7 +315,7 @@ generated already and epsilon ε is handy on the stack…


Thun Documentation by Simon Forman is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
Based on a work at https://osdn.net/projects/joypy/. - Created using Sphinx 1.7.3. + Created using Sphinx 4.3.0.
diff --git a/docs/sphinx_docs/_build/html/notebooks/NoUpdates.html b/docs/sphinx_docs/_build/html/notebooks/NoUpdates.html index 56080bc..65498ab 100644 --- a/docs/sphinx_docs/_build/html/notebooks/NoUpdates.html +++ b/docs/sphinx_docs/_build/html/notebooks/NoUpdates.html @@ -1,19 +1,18 @@ - + - + - - + + + No Updates — Thun 0.4.1 documentation - - - - - - - + + + + + + @@ -30,39 +29,82 @@
+ +
-
+

No Updates

DRAFT

    -
  1. Joy doesn’t need to change.
  2. +
  3. Joy doesn’t need to change.

    -
  1. The interpreter doesn’t need to change, viewer function can customize mainloop. Or use a sub-interpreter (Joy in Joy.) The base interpreter remains static.
  2. -
  3. Once a function has been named and defined never change that name. It’s just not allowed. If you need to change a function foo you have to call it foo_II or something. Once a function (name mapped to behavior) is released to the public that’s it, it’s done.
  4. -
  5. The language evolves by adding new definitions and refactoring, always choosing new names for new functions.
  6. +
  7. The interpreter doesn’t need to change, viewer function can customize mainloop. Or use a sub-interpreter (Joy in Joy.) The base interpreter remains static.

  8. +
  9. Once a function has been named and defined never change that name. It’s just not allowed. If you need to change a function foo you have to call it foo_II or something. Once a function (name mapped to behavior) is released to the public that’s it, it’s done.

  10. +
  11. The language evolves by adding new definitions and refactoring, always choosing new names for new functions.

    -
  1. Following Semantic Versioning there will never be a version 2.0.
  2. +
  3. Following Semantic Versioning there will never be a version 2.0.

    -
  1. Major version must be incremented if any backwards incompatible changes are introduced to the public API.
  2. -
  3. We never implement any backwards incompatible changes, so…
  4. -
  5. We could see e.g. Thun version 1.273.3!
  6. +
  7. Major version must be incremented if any backwards incompatible changes are introduced to the public API.

  8. +
  9. We never implement any backwards incompatible changes, so…

  10. +
  11. We could see e.g. Thun version 1.273.3!

-
+
+
-
+

The base case []

As before, the stopping predicate just has to detect the empty list:

Tree-get == [pop not] [E] [R0] [R1] genrec
@@ -751,8 +752,8 @@ Joy.

[pop not] [E] [R0] [R1] genrec
 
-
-
+
+

Node case [key value left right]

Now we need to figure out R0 and R1:

[key value left right] key R0 [BTree-get] R1
@@ -762,10 +763,10 @@ Joy.

are the same return the value, otherwise recur on one of the child nodes. So it’s very similar to the above funtion, with [R0] == [] and R1 == P [T>] [E] [T<] cmp:

-
[key value left right] key [BTree-get] P [T>] [E] [T<] cmp
+
[key value left right] key [BTree-get] P [T>] [E] [T<] cmp
 
-
+

Predicate

P == over [get-node-key] nullary
 get-node-key == pop popop first
@@ -773,8 +774,8 @@ and R1 
 

The only difference is that get-node-key does one less pop because there’s no value to discard.

-
-
+
+

Branches

Now we have to derive the branches:

[key_n value_n left right] key [BTree-get] T>
@@ -782,8 +783,8 @@ because there’s no value to discard.

[key_n value_n left right] key [BTree-get] T<
-
-
+
+

Greater than and less than

The cases of T> and T< are similar to above but instead of using infra we have to discard the rest of the structure:

@@ -810,8 +811,8 @@ because there’s no value to discard.

right key BTree-get
- -
+ +

Equal keys

Return the node’s value:

[key_n value_n left right] key [BTree-get] E == value_n
@@ -819,9 +820,9 @@ because there’s no value to discard.

E == popop second
-
- -
+ + +

Tree-get

So:

fourth == rest rest rest first
@@ -831,65 +832,65 @@ because there’s no value to discard.

T< == [third] dipd i E == popop second -Tree-get == [pop not] swap [] [P [T>] [E] [T<] cmp] genrec +Tree-get == [pop not] swap [] [P [T>] [E] [T<] cmp] genrec
-
# I don't want to deal with name conflicts with the above so I'm inlining everything here.
-# The original Joy system has "hide" which is a meta-command which allows you to use named
-# definitions that are only in scope for a given definition.  I don't want to implement
-# that (yet) so...
+
# I don't want to deal with name conflicts with the above so I'm inlining everything here.
+# The original Joy system has "hide" which is a meta-command which allows you to use named
+# definitions that are only in scope for a given definition.  I don't want to implement
+# that (yet) so...
 
 
-define('''
-Tree-get == [pop not] swap [] [
-  over [pop popop first] nullary
-  [[fourth] dipd i]
-  [popop second]
-  [[third] dipd i]
-  cmp
-  ] genrec
-''')
+define('''
+Tree-get == [pop not] swap [] [
+  over [pop popop first] nullary
+  [[fourth] dipd i]
+  [popop second]
+  [[third] dipd i]
+  cmp
+  ] genrec
+''')
 
-
J('["gary" 23 [] []] "mike" [popd " not in tree" +] Tree-get')
+
J('["gary" 23 [] []] "mike" [popd " not in tree" +] Tree-get')
 
'mike not in tree'
 
-
J('["gary" 23 [] []] "gary" [popop "err"] Tree-get')
+
J('["gary" 23 [] []] "gary" [popop "err"] Tree-get')
 
23
 
-
J('''
+
J('''
 
-    [] [[0 'a'] [1 'b'] [2 'c']] [i Tree-add] step
+    [] [[0 'a'] [1 'b'] [2 'c']] [i Tree-add] step
 
-    'c' [popop 'not found'] Tree-get
+    'c' [popop 'not found'] Tree-get
 
-''')
+''')
 
2
 
-
J('''
+
J('''
 
-    [] [[0 'a'] [1 'b'] [2 'c']] [i Tree-add] step
+    [] [[0 'a'] [1 'b'] [2 'c']] [i Tree-add] step
 
-    'd' [popop 'not found'] Tree-get
+    'd' [popop 'not found'] Tree-get
 
-''')
+''')
 
'not found'
 
-
-
-
+
+ +

Tree-delete

Now let’s write a function that can return a tree datastructure with a key, value pair deleted:

@@ -899,14 +900,14 @@ key, value pair deleted:

If the key is not in tree it just returns the tree unchanged.

-
+

Base case

Same as above.

Tree-Delete == [pop not] [pop] [R0] [R1] genrec
 
-
-
+ +

Recur

Now we get to figure out the recursive case. We need the node’s key to compare and we need to carry the key into recursive branches. Let D @@ -931,14 +932,14 @@ be shorthand for Tr

So:

R0 == over first swap dup
-R1 == cons roll> [T>] [E] [T<] cmp
+R1 == cons roll> [T>] [E] [T<] cmp
 
- -
+ +

Compare Keys

The last line above:

-
[node_key node_value left right] [key D] node_key key [T>] [E] [T<] cmp
+
[node_key node_value left right] [key D] node_key key [T>] [E] [T<] cmp
 

Then becomes one of these three:

@@ -947,8 +948,8 @@ be shorthand for Tr [node_key node_value left right] [key D] T<
-
-
+ +

Greater than case and less than case

   [node_key node_value left right] [F] T>
 -------------------------------------------------
@@ -975,8 +976,8 @@ be shorthand for Tr
 T< == [dipdd] cons infra
 
-
-
+ +

The else case

We have found the node in the tree where key equals node_key. We need to replace the current node with something

@@ -986,7 +987,7 @@ need to replace the current node with something

We have to handle three cases, so let’s use cond.

-
+

One or more child nodes are []

The first two cases are symmetrical: if we only have one non-empty child node return it. If both child nodes are empty return an empty node.

@@ -997,8 +998,8 @@ node return it. If both child nodes are empty return an empty node.

] cond
- -
+ +

Both child nodes are non-empty.

If both child nodes are non-empty, we find the highest node in our lower sub-tree, take its key and value to replace (delete) our own, then get @@ -1024,8 +1025,8 @@ right left [key D] node_value node_key popop E″ right left [key D] E″

- -
+ +

We have to we find the highest (right-most) node in our lower (left) sub-tree:

right left [key D] E″
 
@@ -1075,8 +1076,8 @@ rightest W′
W.rightmost == [?fourth] [fourth] while
 
-
-
+
+

Found right-most node in our left sub-tree

We know rightest is not empty:

[R_key R_value R_left R_right] W′
@@ -1096,8 +1097,8 @@ R_key R_value
 right left R_key R_value [D] E⁗
 
-
-
+ +

Replace current node key and value, recursively delete rightmost

Final stretch. We want to end up with something like:

right left [R_key D] i R_value R_key
@@ -1150,9 +1151,9 @@ E == [
 ] cond
 
-
- -
+ + +

Refactoring

W.rightmost == [fourth] [fourth] while
 W.unpack == uncons uncons pop
@@ -1168,75 +1169,75 @@ E == [
 T> == [dipd] cons infra
 T< == [dipdd] cons infra
 R0 == over first swap dup
-R1 == cons roll> [T>] [E] [T<] cmp
+R1 == cons roll> [T>] [E] [T<] cmp
 BTree-Delete == [pop not] swap [R0] [R1] genrec
 

By the standards of the code I’ve written so far, this is a huge Joy program.

-
DefinitionWrapper.add_definitions('''
-first_two == uncons uncons pop
-fourth == rest rest rest first
-?fourth == [] [fourth] [] ifte
-W.rightmost == [?fourth] [fourth] while
-E.clear_stuff == roll> popop rest
-E.delete == cons dipd
-W == dup W.rightmost first_two over
-E.0 == E.clear_stuff [W] dip E.delete swap
-E == [[[pop third not] pop fourth] [[pop fourth not] pop third] [[E.0] cons infra]] cond
-T> == [dipd] cons infra
-T< == [dipdd] cons infra
-R0 == over first swap dup
-R1 == cons roll> [T>] [E] [T<] cmp
-Tree-Delete == [pop not] [pop] [R0] [R1] genrec
-''', D)
+
DefinitionWrapper.add_definitions('''
+first_two == uncons uncons pop
+fourth == rest rest rest first
+?fourth == [] [fourth] [] ifte
+W.rightmost == [?fourth] [fourth] while
+E.clear_stuff == roll> popop rest
+E.delete == cons dipd
+W == dup W.rightmost first_two over
+E.0 == E.clear_stuff [W] dip E.delete swap
+E == [[[pop third not] pop fourth] [[pop fourth not] pop third] [[E.0] cons infra]] cond
+T> == [dipd] cons infra
+T< == [dipdd] cons infra
+R0 == over first swap dup
+R1 == cons roll> [T>] [E] [T<] cmp
+Tree-Delete == [pop not] [pop] [R0] [R1] genrec
+''', D)
 
-
J("['a' 23 [] ['b' 88 [] ['c' 44 [] []]]] 'c' Tree-Delete ")
+
J("['a' 23 [] ['b' 88 [] ['c' 44 [] []]]] 'c' Tree-Delete ")
 
['a' 23 [] ['b' 88 [] []]]
 
-
J("['a' 23 [] ['b' 88 [] ['c' 44 [] []]]] 'b' Tree-Delete ")
+
J("['a' 23 [] ['b' 88 [] ['c' 44 [] []]]] 'b' Tree-Delete ")
 
['a' 23 [] ['c' 44 [] []]]
 
-
J("['a' 23 [] ['b' 88 [] ['c' 44 [] []]]] 'a' Tree-Delete ")
+
J("['a' 23 [] ['b' 88 [] ['c' 44 [] []]]] 'a' Tree-Delete ")
 
['b' 88 [] ['c' 44 [] []]]
 
-
J("['a' 23 [] ['b' 88 [] ['c' 44 [] []]]] 'der' Tree-Delete ")
+
J("['a' 23 [] ['b' 88 [] ['c' 44 [] []]]] 'der' Tree-Delete ")
 
['a' 23 [] ['b' 88 [] ['c' 44 [] []]]]
 
-
J('[] [4 2 3 1 6 7 5 ] [0 swap Tree-add] step')
+
J('[] [4 2 3 1 6 7 5 ] [0 swap Tree-add] step')
 
[4 0 [2 0 [1 0 [] []] [3 0 [] []]] [6 0 [5 0 [] []] [7 0 [] []]]]
 
-
J("[4 0 [2 0 [1 0 [] []] [3 0 [] []]] [6 0 [5 0 [] []] [7 0 [] []]]] 3 Tree-Delete ")
+
J("[4 0 [2 0 [1 0 [] []] [3 0 [] []]] [6 0 [5 0 [] []] [7 0 [] []]]] 3 Tree-Delete ")
 
[4 0 [2 0 [1 0 [] []] []] [6 0 [5 0 [] []] [7 0 [] []]]]
 
-
J("[4 0 [2 0 [1 0 [] []] [3 0 [] []]] [6 0 [5 0 [] []] [7 0 [] []]]] 4 Tree-Delete ")
+
J("[4 0 [2 0 [1 0 [] []] [3 0 [] []]] [6 0 [5 0 [] []] [7 0 [] []]]] 4 Tree-Delete ")
 
[3 0 [2 0 [1 0 [] []] []] [6 0 [5 0 [] []] [7 0 [] []]]]
 
-
-
-
+
+ +

Appendix: The source code.

fourth == rest_two rest first
 ?fourth == [] [fourth] [] ifte
@@ -1283,91 +1284,55 @@ Tree-get == [pop not] swap [] [_Tree_get_R] genrec
 Tree-delete == [pop not] [pop] [_Tree_delete_R0] [_Tree_delete_R1] genrec
 
-
- + + +
@@ -1408,7 +1372,7 @@ Tree-delete == [pop not] [pop] [_Tree_delete_R0] [_Tree_delete_R1] genrec
Thun Documentation by Simon Forman is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
Based on a work at https://osdn.net/projects/joypy/. - Created using Sphinx 1.7.3. + Created using Sphinx 4.3.0. diff --git a/docs/sphinx_docs/_build/html/notebooks/Quadratic.html b/docs/sphinx_docs/_build/html/notebooks/Quadratic.html index b6a2169..e617c60 100644 --- a/docs/sphinx_docs/_build/html/notebooks/Quadratic.html +++ b/docs/sphinx_docs/_build/html/notebooks/Quadratic.html @@ -1,19 +1,19 @@ - + - + - - + + + Quadratic formula — Thun 0.4.1 documentation - - - - - - - + + + + + + + @@ -30,12 +30,14 @@
+ +
-
from notebook_preamble import J, V, define
+  
from notebook_preamble import J, V, define
 
-
+

Quadratic formula

Cf. jp-quadratic.html

@@ -45,47 +47,47 @@

\(\frac{-b \pm \sqrt{b^2 - 4ac}}{2a}\)

-
+

Write a straightforward program with variable names.

This math translates to Joy code in a straightforward manner. We are going to use named variables to keep track of the arguments, then write a definition without them.

-
+

-b

b neg
 
-
-
+
+

sqrt(b^2 - 4 * a * c)

b sqr 4 a c * * - sqrt
 
-
-
+ +

/2a

a 2 * /
 
-
-
+ +

±

There is a function pm that accepts two values on the stack and replaces them with their sum and difference.

pm == [+] [-] cleave popdd
 
-
-
+ +

Putting Them Together

b neg b sqr 4 a c * * - sqrt pm a 2 * [/] cons app2
 

We use app2 to compute both roots by using a quoted program [2a /] built with cons.

-
-
-
+ + +

Derive a definition.

Working backwards we use dip and dipd to extract the code from the variables:

@@ -98,11 +100,11 @@ the variables:

The three arguments are to the left, so we can “chop off” everything to the right and say it’s the definition of the quadratic function:

-
define('quadratic == over [[[neg] dupdip sqr 4] dipd * * - sqrt pm] dip 2 * [/] cons app2')
+
define('quadratic == over [[[neg] dupdip sqr 4] dipd * * - sqrt pm] dip 2 * [/] cons app2')
 

Let’s try it out:

-
J('3 1 1 quadratic')
+
J('3 1 1 quadratic')
 
-0.3819660112501051 -2.618033988749895
@@ -112,7 +114,7 @@ the right and say it’s the definition of the dip and dipd combinators building the main program
 by incorporating the values on the stack. Then that program runs and you
 get the results. This is pretty typical of Joy code.

-
V('-5 1 4 quadratic')
+
V('-5 1 4 quadratic')
 
                                                   . -5 1 4 quadratic
@@ -162,30 +164,55 @@ get the results. This is pretty typical of Joy code.

4.0 1.0 .
-
-
+ +
+
@@ -226,7 +252,7 @@ get the results. This is pretty typical of Joy code.


Thun Documentation by Simon Forman is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
Based on a work at https://osdn.net/projects/joypy/. - Created using Sphinx 1.7.3. + Created using Sphinx 4.3.0.
diff --git a/docs/sphinx_docs/_build/html/notebooks/Recursion_Combinators.html b/docs/sphinx_docs/_build/html/notebooks/Recursion_Combinators.html index 977b2bb..f6a7fb3 100644 --- a/docs/sphinx_docs/_build/html/notebooks/Recursion_Combinators.html +++ b/docs/sphinx_docs/_build/html/notebooks/Recursion_Combinators.html @@ -1,19 +1,18 @@ - + - + - - + + + Recursion Combinators — Thun 0.4.1 documentation - - - - - - - + + + + + + @@ -30,12 +29,14 @@
+ +
-
from notebook_preamble import D, DefinitionWrapper, J, V, define
+  
from notebook_preamble import D, DefinitionWrapper, J, V, define
 
-
+

Recursion Combinators

This article describes the genrec combinator, how to use it, and several generic specializations.

@@ -46,7 +47,7 @@ several generic specializations.

From “Recursion Theory and Joy” (j05cmp.html) by Manfred von Thun:

-
“The genrec combinator takes four program parameters in addition to +

“The genrec combinator takes four program parameters in addition to whatever data parameters it needs. Fourth from the top is an if-part, followed by a then-part. If the if-part yields true, then the then-part is executed and the combinator terminates. The other two @@ -55,8 +56,9 @@ false, the rec1-part is executed. Following that the four program parameters and the combinator are again pushed onto the stack bundled up in a quoted form. Then the rec2-part is executed, where it will find the bundled form. Typically it will then execute the bundled -form, either with i or with app2, or some other combinator.”

-
+form, either with i or with app2, or some other combinator.”

+
+

Designing Recursive Functions

The way to design one of these is to fix your base case and test and then treat R1 and R2 as an else-part “sandwiching” a quotation @@ -73,8 +75,8 @@ from:

Set the stack arguments in front and figure out what R1 and R2 have to do to apply the quoted [F] in the proper way.

-
-
+ +

Primitive Recursive Functions

Primitive recursive functions are those where R2 == i.

P == [I] [T] [R] primrec
@@ -82,35 +84,35 @@ have to do to apply the quoted == [I] [T] [R P] ifte
 
-
-
+ +

Hylomorphism

A hylomorphism is a recursive function H :: A -> C that converts a value of type A into a value of type C by means of:

    -
  • A generator G :: A -> (B, A)
  • -
  • A combiner F :: (B, C) -> C
  • -
  • A predicate P :: A -> Bool to detect the base case
  • -
  • A base case value c :: C
  • -
  • Recursive calls (zero or more); it has a “call stack in the form of a -cons list”.
  • +
  • A generator G :: A -> (B, A)

  • +
  • A combiner F :: (B, C) -> C

  • +
  • A predicate P :: A -> Bool to detect the base case

  • +
  • A base case value c :: C

  • +
  • Recursive calls (zero or more); it has a “call stack in the form of a +cons list”.

It may be helpful to see this function implemented in imperative Python code.

-
def hylomorphism(c, F, P, G):
-    '''Return a hylomorphism function H.'''
+
def hylomorphism(c, F, P, G):
+    '''Return a hylomorphism function H.'''
 
-    def H(a):
-        if P(a):
-            result = c
-        else:
-            b, aa = G(a)
-            result = F(b, H(aa))  # b is stored in the stack frame during recursive call to H().
-        return result
+    def H(a):
+        if P(a):
+            result = c
+        else:
+            b, aa = G(a)
+            result = F(b, H(aa))  # b is stored in the stack frame during recursive call to H().
+        return result
 
-    return H
+    return H
 

Cf. “Bananas, Lenses, & Barbed @@ -118,8 +120,8 @@ Wire”

Note that during evaluation of H() the intermediate b values are stored in the Python call stack. This is what is meant by “call stack in the form of a cons list”.

-
-
+
+

Hylomorphism in Joy

We can define a combinator hylomorphism that will make a hylomorphism combinator H from constituent parts.

@@ -155,8 +157,8 @@ it is a simple specialization of the general recursion combinator.

H == [P] c [G] [F] hylomorphism == [P] [pop c] [G] [dip F] genrec
 
-
-
+ +

Derivation of hylomorphism combinator

Now we just need to derive a definition that builds the genrec arguments out of the pieces given to the hylomorphism combinator.

@@ -167,9 +169,9 @@ arguments out of the pieces given to the -
  • Use swoncat twice to decouple [c] and [F].
  • -
  • Use unit to dequote c.
  • -
  • Use dipd to untangle [unit [pop] swoncat] from the givens.
  • +
  • Use swoncat twice to decouple [c] and [F].

  • +
  • Use unit to dequote c.

  • +
  • Use dipd to untangle [unit [pop] swoncat] from the givens.

  • So:

    H == [P] [pop c]              [G]                  [dip F] genrec
    @@ -183,40 +185,40 @@ the left so we have a definition for 
     
    hylomorphism == [unit [pop] swoncat] dipd [dip] swoncat genrec
     
    -
    define('hylomorphism == [unit [pop] swoncat] dipd [dip] swoncat genrec')
    +
    define('hylomorphism == [unit [pop] swoncat] dipd [dip] swoncat genrec')
     
    -
    +

    Example: Finding Triangular Numbers

    Let’s write a function that, given a positive integer, returns the sum of all positive integers less than that one. (In this case the types A, B and C are all int.)

    To sum a range of integers from 0 to n - 1:

      -
    • [P] is [1 <=]
    • -
    • c is 0
    • -
    • [G] is [-- dup]
    • -
    • [F] is [+]
    • +
    • [P] is [1 <=]

    • +
    • c is 0

    • +
    • [G] is [-- dup]

    • +
    • [F] is [+]

    -
    define('triangular_number == [1 <=] 0 [-- dup] [+] hylomorphism')
    +
    define('triangular_number == [1 <=] 0 [-- dup] [+] hylomorphism')
     

    Let’s try it:

    -
    J('5 triangular_number')
    +
    J('5 triangular_number')
     
    10
     
    -
    J('[0 1 2 3 4 5 6] [triangular_number] map')
    +
    J('[0 1 2 3 4 5 6] [triangular_number] map')
     
    [0 0 1 3 6 10 15]
     
    -
    -
    -
    +
    +
    +

    Four Specializations

    There are at least four kinds of recursive combinator, depending on two choices. The first choice is whether the combiner function F should @@ -244,7 +246,7 @@ Consider the recursive branches:

    The following four sections illustrate how these work, omitting the predicate evaluation.

    -
    +

    H1

    H1 == [P] [pop c] [G] [dip F] genrec
     
    @@ -271,8 +273,8 @@ the intermediate results along with the pending combiner functions. When the base case is reached the last term is replaced by the identity value c and the continuation “collapses” into the final result using the combiner F.

    -
    -
    +
    +

    H2

    When you can start with the identity value c on the stack and the combiner F can operate as you go using the intermediate results @@ -297,8 +299,8 @@ reverse order.

    ... d″
    -
    -
    + +

    H3

    If you examine the traces above you’ll see that the combiner F only gets to operate on the results of G, it never “sees” the first value @@ -326,8 +328,8 @@ one item instead of two (the b is instead the duplicate of a.)

    ... d″
    -
    -
    + +

    H4

    And, last but not least, if you can combine as you go, starting with c, and the combiner F needs to work on the current item, this is @@ -350,9 +352,9 @@ the form:

    ... d″
    -
    -
    -
    + + +

    Anamorphism

    An anamorphism can be defined as a hylomorphism that uses [] for c and swons for F. An anamorphic function builds a list of @@ -360,68 +362,68 @@ values.

    A == [P] [] [G] [swons] hylomorphism
     
    -
    -

    range et. al. An example of an anamorphism is the range function which generates the list of integers from 0 to n - 1 given n.

    +
    +

    range et. al. An example of an anamorphism is the range function which generates the list of integers from 0 to n - 1 given n.

    Each of the above variations can be used to make four slightly different range functions.

    -
    +

    range with H1

    H1 == [P]    [pop c]  [G]      [dip F]     genrec
        == [0 <=] [pop []] [-- dup] [dip swons] genrec
     
    -
    define('range == [0 <=] [] [-- dup] [swons] hylomorphism')
    +
    define('range == [0 <=] [] [-- dup] [swons] hylomorphism')
     
    -
    J('5 range')
    +
    J('5 range')
     
    [4 3 2 1 0]
     
    -
    -
    +
    +

    range with H2

    H2 == c  swap [P]    [pop] [G      [F]     dip] primrec
        == [] swap [0 <=] [pop] [-- dup [swons] dip] primrec
     
    -
    define('range_reverse == [] swap [0 <=] [pop] [-- dup [swons] dip] primrec')
    +
    define('range_reverse == [] swap [0 <=] [pop] [-- dup [swons] dip] primrec')
     
    -
    J('5 range_reverse')
    +
    J('5 range_reverse')
     
    [0 1 2 3 4]
     
    -
    -
    +
    +

    range with H3

    H3 == [P]    [pop c]  [[G]  dupdip] [dip F]     genrec
        == [0 <=] [pop []] [[--] dupdip] [dip swons] genrec
     
    -
    define('ranger == [0 <=] [pop []] [[--] dupdip] [dip swons] genrec')
    +
    define('ranger == [0 <=] [pop []] [[--] dupdip] [dip swons] genrec')
     
    -
    J('5 ranger')
    +
    J('5 ranger')
     
    [5 4 3 2 1]
     
    -
    -
    +
    +

    range with H4

    H4 == c  swap [P]    [pop] [[F]     dupdip G ] primrec
        == [] swap [0 <=] [pop] [[swons] dupdip --] primrec
     
    -
    define('ranger_reverse == [] swap [0 <=] [pop] [[swons] dupdip --] primrec')
    +
    define('ranger_reverse == [] swap [0 <=] [pop] [[swons] dupdip --] primrec')
     
    -
    J('5 ranger_reverse')
    +
    J('5 ranger_reverse')
     
    [1 2 3 4 5]
    @@ -430,10 +432,10 @@ values.

    Hopefully this illustrates the workings of the variations. For more insight you can run the cells using the V() function instead of the J() function to get a trace of the Joy evaluation.

    -
    -
    -
    -
    +
    +
    +
    +

    Catamorphism

    A catamorphism can be defined as a hylomorphism that uses [uncons swap] for [G] and [[] =] (or just [not]) for the @@ -442,27 +444,27 @@ and makes some new value.

    C == [not] c [uncons swap] [F] hylomorphism
     
    -
    define('swuncons == uncons swap')  # Awkward name.
    +
    define('swuncons == uncons swap')  # Awkward name.
     

    An example of a catamorphism is the sum function.

    sum == [not] 0 [swuncons] [+] hylomorphism
     
    -
    define('sum == [not] 0 [swuncons] [+] hylomorphism')
    +
    define('sum == [not] 0 [swuncons] [+] hylomorphism')
     
    -
    J('[5 4 3 2 1] sum')
    +
    J('[5 4 3 2 1] sum')
     
    15
     
    -
    +

    The step combinator

    The step combinator will usually be better to use than catamorphism.

    -
    J('[step] help')
    +
    J('[step] help')
     
    Run a quoted program on each item in a sequence.
    @@ -486,18 +488,18 @@ and makes some new value.

    on top of the stack.
    -
    define('sum == 0 swap [+] step')
    +
    define('sum == 0 swap [+] step')
     
    -
    J('[5 4 3 2 1] sum')
    +
    J('[5 4 3 2 1] sum')
     
    15
     
    -
    -
    -
    +
    +
    +

    Example: Factorial Function

    For the Factorial function:

    H4 == c swap [P] [pop] [[F] dupdip G] primrec
    @@ -510,17 +512,17 @@ and makes some new value.

    P == 1 <=
    -
    define('factorial == 1 swap [1 <=] [pop] [[*] dupdip --] primrec')
    +
    define('factorial == 1 swap [1 <=] [pop] [[*] dupdip --] primrec')
     
    -
    J('5 factorial')
    +
    J('5 factorial')
     
    120
     
    -
    -
    +
    +

    Example: tails

    An example of a paramorphism for lists given in the “Bananas…” paper @@ -542,37 +544,37 @@ pattern H2P == not

    -
    define('tails == [] swap [not] [pop] [rest dup [swons] dip] primrec')
    +
    define('tails == [] swap [not] [pop] [rest dup [swons] dip] primrec')
     
    -
    J('[1 2 3] tails')
    +
    J('[1 2 3] tails')
     
    [[] [3] [2 3]]
     
    -
    -
    + +

    Conclusion: Patterns of Recursion

    Our story so far…

    -
    +

    Hylo-, Ana-, Cata-

    H == [P  ] [pop c ] [G          ] [dip F        ] genrec
     A == [P  ] [pop []] [G          ] [dip swap cons] genrec
     C == [not] [pop c ] [uncons swap] [dip F        ] genrec
     
    -
    -
    +
    +

    Para-, ?-, ?-

    P == c  swap [P  ] [pop] [[F        ] dupdip G          ] primrec
     ? == [] swap [P  ] [pop] [[swap cons] dupdip G          ] primrec
     ? == c  swap [not] [pop] [[F        ] dupdip uncons swap] primrec
     
    -
    -
    -
    + + +

    Appendix: Fun with Symbols

    |[ (c, F), (G, P) ]| == (|c, F|) • [(G, P)]
     
    @@ -584,58 +586,55 @@ Wire”

    I think they are having slightly too much fun with the symbols. However, “Too much is always better than not enough.”

    -
    -
    + +
    +
    @@ -676,7 +674,7 @@ Wire”


    Thun Documentation by Simon Forman is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
    Based on a work at https://osdn.net/projects/joypy/. - Created using Sphinx 1.7.3. + Created using Sphinx 4.3.0. diff --git a/docs/sphinx_docs/_build/html/notebooks/Replacing.html b/docs/sphinx_docs/_build/html/notebooks/Replacing.html index de93cab..f152c0e 100644 --- a/docs/sphinx_docs/_build/html/notebooks/Replacing.html +++ b/docs/sphinx_docs/_build/html/notebooks/Replacing.html @@ -1,19 +1,18 @@ - + - + - - + + + Replacing Functions in the Dictionary — Thun 0.4.1 documentation - - - - - - - + + + + + + @@ -30,23 +29,25 @@
    + +
    -
    +

    Replacing Functions in the Dictionary

    For now, there is no way to define new functions from within the Joy language. All functions (and the interpreter) all accept and return a dictionary parameter (in addition to the stack and expression) so that -we can implement e.g. a function that adds new functions to the +we can implement e.g. a function that adds new functions to the dictionary. However, there’s no function that does that. Adding a new function to the dictionary is a meta-interpreter action, you have to do it in Python, not Joy.

    -
    from notebook_preamble import D, J, V
    +
    from notebook_preamble import D, J, V
     
    -
    +

    A long trace

    -
    V('[23 18] average')
    +
    V('[23 18] average')
     
                                      . [23 18] average
    @@ -93,8 +94,8 @@ it in Python, not Joy.

    20.5 .
    -
    -
    +
    +

    Replacing size with a Python version

    Both sum and size each convert a sequence to a single value.

     sum == 0 swap [+] step
    @@ -104,30 +105,30 @@ it in Python, not Joy.

    An efficient sum function is already in the library. But for size we can use a “compiled” version hand-written in Python to speed up evaluation and make the trace more readable.

    -
    from joy.library import SimpleFunctionWrapper
    -from joy.utils.stack import iter_stack
    +
    from joy.library import SimpleFunctionWrapper
    +from joy.utils.stack import iter_stack
     
     
    -@SimpleFunctionWrapper
    -def size(stack):
    -    '''Return the size of the sequence on the stack.'''
    -    sequence, stack = stack
    -    n = 0
    -    for _ in iter_stack(sequence):
    -        n += 1
    -    return n, stack
    +@SimpleFunctionWrapper
    +def size(stack):
    +    '''Return the size of the sequence on the stack.'''
    +    sequence, stack = stack
    +    n = 0
    +    for _ in iter_stack(sequence):
    +        n += 1
    +    return n, stack
     

    Now we replace the old version in the dictionary with the new version, and re-evaluate the expression.

    -
    D['size'] = size
    +
    D['size'] = size
     
    -
    -
    +
    +

    A shorter trace

    You can see that size now executes in a single step.

    -
    V('[23 18] average')
    +
    V('[23 18] average')
     
                                      . [23 18] average
    @@ -161,24 +162,55 @@ and re-evaluate the expression.

    20.5 .
    -
    -
    +
    +
    +
    @@ -219,7 +250,7 @@ and re-evaluate the expression.


    Thun Documentation by Simon Forman is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
    Based on a work at https://osdn.net/projects/joypy/. - Created using Sphinx 1.7.3. + Created using Sphinx 4.3.0.
    diff --git a/docs/sphinx_docs/_build/html/notebooks/The_Four_Operations.html b/docs/sphinx_docs/_build/html/notebooks/The_Four_Operations.html index ea7e116..1554228 100644 --- a/docs/sphinx_docs/_build/html/notebooks/The_Four_Operations.html +++ b/docs/sphinx_docs/_build/html/notebooks/The_Four_Operations.html @@ -1,19 +1,18 @@ - + - + - - + + + The Four Fundamental Operations of Definite Action — Thun 0.4.1 documentation - - - - - - - + + + + + + @@ -30,19 +29,21 @@
    + +
    -
    +

    The Four Fundamental Operations of Definite Action

    All definite actions (computer program) can be defined by four fundamental patterns of combination:

      -
    1. Sequence
    2. -
    3. Branch
    4. -
    5. Loop
    6. -
    7. Parallel
    8. +
    9. Sequence

    10. +
    11. Branch

    12. +
    13. Loop

    14. +
    15. Parallel

    -
    +

    Sequence

    Do one thing after another. In joy this is represented by putting two symbols together, juxtaposition:

    @@ -51,8 +52,8 @@ symbols together, juxtaposition:

    Operations have inputs and outputs. The outputs of foo must be compatible in “arity”, type, and shape with the inputs of bar.

    -
    -
    + +

    Branch

    Do one thing or another.

    boolean [F] [T] branch
    @@ -93,7 +94,7 @@ outputs for foofoo T bar
     
    -
    +

    ifte

    Often it will be easier on the programmer to write branching code with the predicate specified in a quote. The ifte combinator provides @@ -115,9 +116,9 @@ evaluate it. I reflect this in the structure of the stack effect comment of branch, it will only accept Boolean values, and in the definition of ifte above by including not in the quote, which also has the effect that the subject quotes are in the proper order for branch.)

    -
    -
    -
    + + +

    Loop

    Do one thing zero or more times.

    boolean [Q] loop
    @@ -168,7 +169,7 @@ boolean flag for the next iteration:

    G G G
    -
    +

    while

    Keep doing B while some predicate P is true. This is convenient as the predicate function is made nullary automatically and @@ -191,9 +192,9 @@ flag for the next iteration:

    [P] nullary [B [P] nullary] loop
    -
    -
    -
    + + +

    Parallel

    The parallel operation indicates that two (or more) functions do not interfere with each other and so can run in parallel. The main @@ -201,10 +202,10 @@ difficulty in this sort of thing is orchestrating the recombining (“join” or “wait”) of the results of the functions after they finish.

    The current implementaions and the following definitions are not actually parallel (yet), but there is no reason they couldn’t be -reimplemented in terms of e.g. Python threads. I am not concerned with +reimplemented in terms of e.g. Python threads. I am not concerned with performance of the system just yet, only the elegance of the code it allows us to write.

    -
    +

    cleave

    Joy has a few parallel combinators, the main one being cleave:

                   ... x [A] [B] cleave
    @@ -225,8 +226,8 @@ Elliott’s “Compiling to Categories” paper, et. al.)

    Just a thought, if you cleave two jobs and one requires more time to finish than the other you’d like to be able to assign resources accordingly so that they both finish at the same time.

    -
    -
    +
    +

    “Apply” Functions

    There are also app2 and app3 which run a single quote on more than one value:

    @@ -257,8 +258,8 @@ value.)

    a b
    -
    -
    + +

    map

    The common map function in Joy should also be though of as a parallel operator:

    @@ -266,9 +267,9 @@ value.)

    There is no reason why the implementation of map couldn’t distribute -the Q function over e.g. a pool of worker CPUs.

    - -
    +the Q function over e.g. a pool of worker CPUs.

    + +

    pam

    One of my favorite combinators, the pam combinator is just:

    pam == [i] map
    @@ -281,21 +282,21 @@ stack and combine their (first) outputs in a result list.

    [ a b c ...]
    -
    -
    + +

    Handling Other Kinds of Join

    The cleave operators and others all have pretty brutal join semantics: everything works and we always wait for every sub-computation. We can imagine a few different potentially useful patterns of “joining” results from parallel combinators.

    -
    +

    first-to-finish

    Thinking about variations of pam there could be one that only returns the first result of the first-to-finish sub-program, or the stack could be replaced by its output stack.

    The other sub-programs would be cancelled.

    -
    -
    +
    +

    “Fulminators”

    Also known as “Futures” or “Promises” (by everybody else. “Fulinators” is what I was going to call them when I was thinking about implementing @@ -312,44 +313,57 @@ with “asyncronous” … events?

    sort of thing works perfectly in Joy code I’m not going to worry about it. (And I think the Categories can deal with it anyhow? Incremental evaluation, yeah?)

    -
    - - - + + + + +
    @@ -390,7 +403,7 @@ evaluation, yeah?)


    Thun Documentation by Simon Forman is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
    Based on a work at https://osdn.net/projects/joypy/. - Created using Sphinx 1.7.3. + Created using Sphinx 4.3.0. diff --git a/docs/sphinx_docs/_build/html/notebooks/Treestep.html b/docs/sphinx_docs/_build/html/notebooks/Treestep.html index 8811efa..a0903ee 100644 --- a/docs/sphinx_docs/_build/html/notebooks/Treestep.html +++ b/docs/sphinx_docs/_build/html/notebooks/Treestep.html @@ -1,19 +1,18 @@ - + - + - - + + + Treating Trees II: treestep — Thun 0.4.1 documentation - - - - - - - + + + + + + @@ -30,9 +29,11 @@
    + +
    -
    +

    Treating Trees II: treestep

    Let’s consider a tree structure, similar to one described “Why functional programming matters” by John @@ -65,7 +66,7 @@ combine the result with map part of C so you can use other combinators.)

    -
    +

    Derive the recursive function.

    We can begin to derive it by finding the ifte stage that genrec will produce.

    @@ -110,8 +111,8 @@ the desired outcome.

    == [not] [B] [uncons [N] dip] [map C] genrec
    -
    -
    + +

    Extract the givens to parameterize the program.

    Working backwards:

    [not] [B]          [uncons [N] dip]                  [map C] genrec
    @@ -134,7 +135,7 @@ the desired outcome.

    The givens are all to the left so we have our definition.

    -
    +

    (alternate) Extract the givens to parameterize the program.

    Working backwards:

    [not] [B]           [uncons [N] dip]            [map C] genrec
    @@ -143,32 +144,32 @@ the desired outcome.

    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    -
    -
    -
    + + +

    Define treestep

    -
    from notebook_preamble import D, J, V, define, DefinitionWrapper
    +
    from notebook_preamble import D, J, V, define, DefinitionWrapper
     
    -
    DefinitionWrapper.add_definitions('''
    +
    DefinitionWrapper.add_definitions('''
     
    -    _treestep_0 == [[not] swap] dip
    -    _treestep_1 == [dip] cons [uncons] swoncat
    -    treegrind == [_treestep_1 _treestep_0] dip genrec
    -    treestep == [map] swoncat treegrind
    +    _treestep_0 == [[not] swap] dip
    +    _treestep_1 == [dip] cons [uncons] swoncat
    +    treegrind == [_treestep_1 _treestep_0] dip genrec
    +    treestep == [map] swoncat treegrind
     
    -''', D)
    +''', D)
     
    -
    -
    +
    +

    Examples

    Consider trees, the nodes of which are integers. We can find the sum of all nodes in a tree with this function:

    sumtree == [pop 0] [] [sum +] treestep
     
    -
    define('sumtree == [pop 0] [] [sum +] treestep')
    +
    define('sumtree == [pop 0] [] [sum +] treestep')
     

    Running this function on an empty tree value gives zero:

    @@ -177,7 +178,7 @@ all nodes in a tree with this function:

    0
    -
    J('[] sumtree')  # Empty tree.
    +
    J('[] sumtree')  # Empty tree.
     
    0
    @@ -191,68 +192,68 @@ all nodes in a tree with this function:

    n+m
    -
    J('[23] sumtree')  # No child trees.
    +
    J('[23] sumtree')  # No child trees.
     
    23
     
    -
    J('[23 []] sumtree')  # Child tree, empty.
    +
    J('[23 []] sumtree')  # Child tree, empty.
     
    23
     
    -
    J('[23 [2 [4]] [3]] sumtree')  # Non-empty child trees.
    +
    J('[23 [2 [4]] [3]] sumtree')  # Non-empty child trees.
     
    32
     
    -
    J('[23 [2 [8] [9]] [3] [4 []]] sumtree')  # Etc...
    +
    J('[23 [2 [8] [9]] [3] [4 []]] sumtree')  # Etc...
     
    49
     
    -
    J('[23 [2 [8] [9]] [3] [4 []]] [pop 0] [] [cons sum] treestep')  # Alternate "spelling".
    +
    J('[23 [2 [8] [9]] [3] [4 []]] [pop 0] [] [cons sum] treestep')  # Alternate "spelling".
     
    49
     
    -
    J('[23 [2 [8] [9]] [3] [4 []]] [] [pop 23] [cons] treestep')  # Replace each node.
    +
    J('[23 [2 [8] [9]] [3] [4 []]] [] [pop 23] [cons] treestep')  # Replace each node.
     
    [23 [23 [23] [23]] [23] [23 []]]
     
    -
    J('[23 [2 [8] [9]] [3] [4 []]] [] [pop 1] [cons] treestep')
    +
    J('[23 [2 [8] [9]] [3] [4 []]] [] [pop 1] [cons] treestep')
     
    [1 [1 [1] [1]] [1] [1 []]]
     
    -
    J('[23 [2 [8] [9]] [3] [4 []]] [] [pop 1] [cons] treestep sumtree')
    +
    J('[23 [2 [8] [9]] [3] [4 []]] [] [pop 1] [cons] treestep sumtree')
     
    6
     
    -
    J('[23 [2 [8] [9]] [3] [4 []]] [pop 0] [pop 1] [sum +] treestep')  # Combine replace and sum into one function.
    +
    J('[23 [2 [8] [9]] [3] [4 []]] [pop 0] [pop 1] [sum +] treestep')  # Combine replace and sum into one function.
     
    6
     
    -
    J('[4 [3 [] [7]]] [pop 0] [pop 1] [sum +] treestep')  # Combine replace and sum into one function.
    +
    J('[4 [3 [] [7]]] [pop 0] [pop 1] [sum +] treestep')  # Combine replace and sum into one function.
     
    3
     
    -
    -
    +
    +

    Redefining the Ordered Binary Tree in terms of treestep.

    Tree = [] | [[key value] left right]
     
    @@ -266,7 +267,7 @@ all nodes in a tree with this function:

    [key value] N [left right] [K] map C
     
    -
    +

    Traversal

    [key value] first [left right] [K] map i
     key [value]       [left right] [K] map i
    @@ -276,7 +277,7 @@ all nodes in a tree with this function:

    This doesn’t quite work:

    -
    J('[[3 0] [[2 0] [][]] [[9 0] [[5 0] [[4 0] [][]] [[8 0] [[6 0] [] [[7 0] [][]]][]]][]]] ["B"] [first] [i] treestep')
    +
    J('[[3 0] [[2 0] [][]] [[9 0] [[5 0] [[4 0] [][]] [[8 0] [[6 0] [] [[7 0] [][]]][]]][]]] ["B"] [first] [i] treestep')
     
    3 'B' 'B'
    @@ -298,15 +299,15 @@ depositing our results directly on the stack.

    [] [first] [flatten cons] treestep
     
    -
    J('[[3 0] [[2 0] [] []] [[9 0] [[5 0] [[4 0] [] []] [[8 0] [[6 0] [] [[7 0] [] []]] []]] []]]   [] [first] [flatten cons] treestep')
    +
    J('[[3 0] [[2 0] [] []] [[9 0] [[5 0] [[4 0] [] []] [[8 0] [[6 0] [] [[7 0] [] []]] []]] []]]   [] [first] [flatten cons] treestep')
     
    [3 2 9 5 4 8 6 7]
     

    There we go.

    -
    -
    +
    +

    In-order traversal

    From here:

    key [[lk] [rk]] C
    @@ -321,15 +322,15 @@ depositing our results directly on the stack.

    [] [i roll< swons concat] [first] treestep
     
    -
    J('[[3 0] [[2 0] [] []] [[9 0] [[5 0] [[4 0] [] []] [[8 0] [[6 0] [] [[7 0] [] []]] []]] []]]   [] [uncons pop] [i roll< swons concat] treestep')
    +
    J('[[3 0] [[2 0] [] []] [[9 0] [[5 0] [[4 0] [] []] [[8 0] [[6 0] [] [[7 0] [] []]] []]] []]]   [] [uncons pop] [i roll< swons concat] treestep')
     
    [2 3 4 5 6 7 8 9]
     
    -
    -
    -
    +
    +
    +

    With treegrind?

    The treegrind function doesn’t include the map combinator, so the [C] function must arrange to use some combinator on the quoted @@ -342,38 +343,38 @@ non-empty node is:

    [key value] N [left right] [K] C
     
    -
    J('[["key" "value"] ["left"] ["right"] ] ["B"] ["N"] ["C"] treegrind')
    +
    J('[["key" "value"] ["left"] ["right"] ] ["B"] ["N"] ["C"] treegrind')
     
    ['key' 'value'] 'N' [['left'] ['right']] [[not] ['B'] [uncons ['N'] dip] ['C'] genrec] 'C'
     
    -
    -
    +
    +

    treegrind with step

    Iteration through the nodes

    -
    J('[[3 0] [[2 0] [] []] [[9 0] [[5 0] [[4 0] [] []] [[8 0] [[6 0] [] [[7 0] [] []]] []]] []]]   [pop] ["N"] [step] treegrind')
    +
    J('[[3 0] [[2 0] [] []] [[9 0] [[5 0] [[4 0] [] []] [[8 0] [[6 0] [] [[7 0] [] []]] []]] []]]   [pop] ["N"] [step] treegrind')
     
    [3 0] 'N' [2 0] 'N' [9 0] 'N' [5 0] 'N' [4 0] 'N' [8 0] 'N' [6 0] 'N' [7 0] 'N'
     

    Sum the nodes’ keys.

    -
    J('0 [[3 0] [[2 0] [] []] [[9 0] [[5 0] [[4 0] [] []] [[8 0] [[6 0] [] [[7 0] [] []]] []]] []]]   [pop] [first +] [step] treegrind')
    +
    J('0 [[3 0] [[2 0] [] []] [[9 0] [[5 0] [[4 0] [] []] [[8 0] [[6 0] [] [[7 0] [] []]] []]] []]]   [pop] [first +] [step] treegrind')
     
    44
     

    Rebuild the tree using map (imitating treestep.)

    -
    J('[[3 0] [[2 0] [] []] [[9 0] [[5 0] [[4 0] [] []] [[8 0] [[6 0] [] [[7 0] [] []]] []]] []]]   [] [[100 +] infra] [map cons] treegrind')
    +
    J('[[3 0] [[2 0] [] []] [[9 0] [[5 0] [[4 0] [] []] [[8 0] [[6 0] [] [[7 0] [] []]] []]] []]]   [] [[100 +] infra] [map cons] treegrind')
     
    [[103 0] [[102 0] [] []] [[109 0] [[105 0] [[104 0] [] []] [[108 0] [[106 0] [] [[107 0] [] []]] []]] []]]
     
    -
    -
    +
    +

    Do we have the flexibility to reimplement Tree-get?

    I think we do:

    [B] [N] [C] treegrind
    @@ -389,12 +390,12 @@ user defined, and the per-node function is just the query key literal:

    Let’s try cmp:

    -
    C == P [T>] [E] [T<] cmp
    +
    C == P [T>] [E] [T<] cmp
     
    -[key value] query_key [left right] [K] P [T>] [E] [T<] cmp
    +[key value] query_key [left right] [K] P [T>] [E] [T<] cmp
     
    -
    +

    The predicate P

    Seems pretty easy (we must preserve the value in case the keys are equal):

    @@ -412,7 +413,7 @@ equal):

    (Possibly with a swap at the end? Or just swap T< and T>.)

    So now:

    -
    [left right] [K] [value] key query_key [T>] [E] [T<] cmp
    +
    [left right] [K] [value] key query_key [T>] [E] [T<] cmp
     

    Becomes one of these three:

    @@ -421,105 +422,118 @@ equal):

    [left right] [K] [value] T<
    -
    -
    +
    +

    E

    Easy.

    E == roll> popop first
     
    -
    -
    + +

    T< and T>

    T< == pop [first] dip i
     T> == pop [second] dip i
     
    -
    -
    -
    + + +

    Putting it together

    T> == pop [first] dip i
     T< == pop [second] dip i
     E == roll> popop first
     P == roll< [roll< uncons swap] dip
     
    -Tree-get == [P [T>] [E] [T<] cmp] treegrind
    +Tree-get == [P [T>] [E] [T<] cmp] treegrind
     

    To me, that seems simpler than the genrec version.

    -
    DefinitionWrapper.add_definitions('''
    +
    DefinitionWrapper.add_definitions('''
     
    -    T> == pop [first] dip i
    -    T< == pop [second] dip i
    -    E == roll> popop first
    -    P == roll< [roll< uncons swap] dip
    +    T> == pop [first] dip i
    +    T< == pop [second] dip i
    +    E == roll> popop first
    +    P == roll< [roll< uncons swap] dip
     
    -    Tree-get == [P [T>] [E] [T<] cmp] treegrind
    +    Tree-get == [P [T>] [E] [T<] cmp] treegrind
     
    -''', D)
    +''', D)
     
    -
    J('''\
    +
    J('''\
     
    -[[3 13] [[2 12] [] []] [[9 19] [[5 15] [[4 14] [] []] [[8 18] [[6 16] [] [[7 17] [] []]] []]] []]]
    +[[3 13] [[2 12] [] []] [[9 19] [[5 15] [[4 14] [] []] [[8 18] [[6 16] [] [[7 17] [] []]] []]] []]]
     
    -[] [5] Tree-get
    +[] [5] Tree-get
     
    -''')
    +''')
     
    15
     
    -
    J('''\
    +
    J('''\
     
    -[[3 13] [[2 12] [] []] [[9 19] [[5 15] [[4 14] [] []] [[8 18] [[6 16] [] [[7 17] [] []]] []]] []]]
    +[[3 13] [[2 12] [] []] [[9 19] [[5 15] [[4 14] [] []] [[8 18] [[6 16] [] [[7 17] [] []]] []]] []]]
     
    -[pop "nope"] [25] Tree-get
    +[pop "nope"] [25] Tree-get
     
    -''')
    +''')
     
    'nope'
     
    -
    -
    +
    +
    +
    @@ -560,7 +573,7 @@ equal):


    Thun Documentation by Simon Forman is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
    Based on a work at https://osdn.net/projects/joypy/. - Created using Sphinx 1.7.3. + Created using Sphinx 4.3.0. diff --git a/docs/sphinx_docs/_build/html/notebooks/TypeChecking.html b/docs/sphinx_docs/_build/html/notebooks/TypeChecking.html index e374f2c..9c4d5bb 100644 --- a/docs/sphinx_docs/_build/html/notebooks/TypeChecking.html +++ b/docs/sphinx_docs/_build/html/notebooks/TypeChecking.html @@ -1,19 +1,18 @@ - + - + - - + + + Type Checking — Thun 0.4.1 documentation - - - - - - - + + + + + + @@ -30,37 +29,39 @@
    + +
    -
    +

    Type Checking

    -
    import logging, sys
    +
    import logging, sys
     
    -logging.basicConfig(
    -  format='%(message)s',
    -  stream=sys.stdout,
    -  level=logging.INFO,
    -  )
    +logging.basicConfig(
    +  format='%(message)s',
    +  stream=sys.stdout,
    +  level=logging.INFO,
    +  )
     
    -
    from joy.utils.types import (
    -    doc_from_stack_effect,
    -    infer,
    -    reify,
    -    unify,
    -    FUNCTIONS,
    -    JoyTypeError,
    -)
    +
    from joy.utils.types import (
    +    doc_from_stack_effect,
    +    infer,
    +    reify,
    +    unify,
    +    FUNCTIONS,
    +    JoyTypeError,
    +)
     
    -
    D = FUNCTIONS.copy()
    -del D['product']
    -globals().update(D)
    +
    D = FUNCTIONS.copy()
    +del D['product']
    +globals().update(D)
     
    -
    +

    An Example

    -
    fi, fo = infer(pop, swap, rolldown, rrest, ccons)[0]
    +
    fi, fo = infer(pop, swap, rolldown, rrest, ccons)[0]
     
    25 (--) ∘ pop swap rolldown rrest ccons
    @@ -71,61 +72,61 @@
     40 ([a4 a5 ...1] a3 a2 a1 -- [a2 a3 ...1]) ∘
     
    -
    print doc_from_stack_effect(fi, fo)
    +
    print doc_from_stack_effect(fi, fo)
     
    ([a4 a5 ...1] a3 a2 a1 -- [a2 a3 ...1])
     
    -
    from joy.parser import text_to_expression
    -from joy.utils.stack import stack_to_string
    +
    from joy.parser import text_to_expression
    +from joy.utils.stack import stack_to_string
     
    -
    e = text_to_expression('0 1 2 [3 4]')  # reverse order
    -print stack_to_string(e)
    +
    e = text_to_expression('0 1 2 [3 4]')  # reverse order
    +print stack_to_string(e)
     
    [3 4] 2 1 0
     
    -
    u = unify(e, fi)[0]
    -u
    +
    u = unify(e, fi)[0]
    +u
     
    {a1: 0, a2: 1, a3: 2, a4: 3, a5: 4, s2: (), s1: ()}
     
    -
    g = reify(u, (fi, fo))
    -print doc_from_stack_effect(*g)
    +
    g = reify(u, (fi, fo))
    +print doc_from_stack_effect(*g)
     
    (... [3 4 ] 2 1 0 -- ... [1 2 ])
     
    -
    -
    +
    +

    Unification Works “in Reverse”

    -
    e = text_to_expression('[2 3]')
    +
    e = text_to_expression('[2 3]')
     
    -
    u = unify(e, fo)[0]  # output side, not input side
    -u
    +
    u = unify(e, fo)[0]  # output side, not input side
    +u
     
    {a2: 2, a3: 3, s2: (), s1: ()}
     
    -
    g = reify(u, (fi, fo))
    -print doc_from_stack_effect(*g)
    +
    g = reify(u, (fi, fo))
    +print doc_from_stack_effect(*g)
     
    (... [a4 a5 ] 3 2 a1 -- ... [2 3 ])
     
    -
    -
    +
    +

    Failing a Check

    -
    fi, fo = infer(dup, mul)[0]
    +
    fi, fo = infer(dup, mul)[0]
     
    25 (--) ∘ dup mul
    @@ -134,40 +135,71 @@
     31 (i1 -- i2) ∘
     
    -
    e = text_to_expression('"two"')
    -print stack_to_string(e)
    +
    e = text_to_expression('"two"')
    +print stack_to_string(e)
     
    'two'
     
    -
    try:
    -    unify(e, fi)
    -except JoyTypeError, err:
    -    print err
    +
    try:
    +    unify(e, fi)
    +except JoyTypeError, err:
    +    print err
     
    Cannot unify 'two' and f1.
     
    -
    -
    +
    +
    +
    @@ -208,7 +239,7 @@
    Thun Documentation by Simon Forman is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
    Based on a work at https://osdn.net/projects/joypy/. - Created using Sphinx 1.7.3. + Created using Sphinx 4.3.0.
    diff --git a/docs/sphinx_docs/_build/html/notebooks/Types.html b/docs/sphinx_docs/_build/html/notebooks/Types.html index 192b714..695d853 100644 --- a/docs/sphinx_docs/_build/html/notebooks/Types.html +++ b/docs/sphinx_docs/_build/html/notebooks/Types.html @@ -1,19 +1,18 @@ - + - + - - + + + The Blissful Elegance of Typing Joy — Thun 0.4.1 documentation - - - - - - - + + + + + + @@ -30,9 +29,11 @@
    + +
    -
    +

    The Blissful Elegance of Typing Joy

    This notebook presents a simple type inferencer for Joy code. It can infer the stack effect of most Joy expressions. It’s built largely by @@ -47,7 +48,7 @@ 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.)

    -
    +

    Part I: Pöial’s Rules

    “Typing Tools for Typeless Stack Languages” by Jaanus Pöial

    @@ -60,7 +61,7 @@ Pöial

    }
    -
    +

    First Rule

    This rule deals with functions (and literals) that put items on the stack (-- d):

    @@ -69,8 +70,8 @@ stack (--
    -
    -
    + +

    Second Rule

    This rule deals with functions that consume items from the stack (a --):

    @@ -79,8 +80,8 @@ stack (--
    -
    -
    + +

    Third Rule

    The third rule is actually two rules. These two rules deal with composing functions when the second one will consume one of items the @@ -105,8 +106,8 @@ algorithm.

    It’s all “stack chatter” and list manipulation so we should be able to deduce its type.

    - -
    + +

    Stack Effect Comments

    Joy function types will be represented by Forth-style stack effect comments. I’m going to use numbers instead of names to keep track of the @@ -122,8 +123,8 @@ reminds me of them):

    These commands alter the stack but don’t “look at” the values so these numbers represent an “Any type”.

    - -
    + +

    pop swap

    (1 --) (1 2 -- 2 1)
     
    @@ -137,8 +138,8 @@ unique among both sides. For this let’s change
    (1 2 0 -- 2 1)
     
    -
    -
    + +

    pop∘swap roll<

    (1 2 0 -- 2 1) (1 2 3 -- 2 3 1)
     
    @@ -177,12 +178,12 @@ terms in the forms:

    And now we have the stack effect comment for pop∘swap∘roll<.

    - -
    + +

    Compiling pop∘swap∘roll<

    The simplest way to “compile” this function would be something like:

    -
    def poswrd(s, e, d):
    -    return rolldown(*swap(*pop(s, e, d)))
    +
    def poswrd(s, e, d):
    +    return rolldown(*swap(*pop(s, e, d)))
     

    However, internally this function would still be allocating tuples @@ -192,17 +193,17 @@ terms in the forms:

    We should be able to directly write out a Python function like:

    -
    def poswrd(stack):
    -    (_, (a, (b, (c, stack)))) = stack
    -    return (c, (b, (a, stack)))
    +
    def poswrd(stack):
    +    (_, (a, (b, (c, stack)))) = stack
    +    return (c, (b, (a, stack)))
     

    This eliminates the internal work of the first version. Because this function only rearranges the stack and doesn’t do any actual processing on the stack items themselves all the information needed to implement it is in the stack effect comment.

    -
    -
    +
    +

    Functions on Stacks

    These are slightly tricky.

    rest ( [1 ...] -- [...] )
    @@ -210,8 +211,8 @@ is in the stack effect comment.

    cons ( 1 [...] -- [1 ...] )
    -
    -
    + +

    pop∘swap∘roll< rest

    (1 2 3 0 -- 3 2 1) ([1 ...] -- [...])
     
    @@ -234,8 +235,8 @@ available index number for the right-side stack effect comment):

    And there we are.

    - -
    + +

    pop∘swap∘roll<∘rest rest

    Let’s do it again.

    ([4 ...] 2 3 0 -- 3 2 [...]) ([1 ...] -- [...])
    @@ -273,8 +274,8 @@ actually pretty easy. I’ll explain below.

    This is the stack effect of pop∘swap∘roll<∘rest∘rest.

    -
    -
    + +

    pop∘swap∘roll<∘rest∘rest cons

    ([4 5 ...] 2 3 1 -- 3 2 [...]) (1 [...] -- [1 ...])
     
    @@ -300,8 +301,8 @@ actually pretty easy. I’ll explain below.

    Done.

    - -
    + +

    pop∘swap∘roll<∘rest∘rest∘cons cons

    One more time.

    ([4 5 ...] 2 3 1 -- 3 [2 ...]) (1 [...] -- [1 ...])
    @@ -334,193 +335,193 @@ actually pretty easy. I’ll explain below.

    From this stack effect comment it should be possible to construct the following Python code:

    -
    def F(stack):
    -    (_, (d, (c, ((a, (b, S0)), stack)))) = stack
    -    return (d, (c, S0)), stack
    +
    def F(stack):
    +    (_, (d, (c, ((a, (b, S0)), stack)))) = stack
    +    return (d, (c, S0)), stack
     
    -
    -
    -
    +
    + +

    Part II: Implementation

    -
    +

    Representing Stack Effect Comments in Python

    I’m going to use pairs of tuples of type descriptors, which will be integers or tuples of type descriptors:

    -
    roll_dn = (1, 2, 3), (2, 3, 1)
    +
    roll_dn = (1, 2, 3), (2, 3, 1)
     
    -pop = (1,), ()
    +pop = (1,), ()
     
    -swap = (1, 2), (2, 1)
    +swap = (1, 2), (2, 1)
     
    -
    -
    +
    +

    compose()

    -
    def compose(f, g):
    +
    def compose(f, g):
     
    -    (f_in, f_out), (g_in, g_out) = f, g
    +    (f_in, f_out), (g_in, g_out) = f, g
     
    -    # First rule.
    -    #
    -    #       (a -- b) (-- d)
    -    #    ---------------------
    -    #         (a -- b d)
    +    # First rule.
    +    #
    +    #       (a -- b) (-- d)
    +    #    ---------------------
    +    #         (a -- b d)
     
    -    if not g_in:
    +    if not g_in:
     
    -        fg_in, fg_out = f_in, f_out + g_out
    +        fg_in, fg_out = f_in, f_out + g_out
     
    -    # Second rule.
    -    #
    -    #       (a --) (c -- d)
    -    #    ---------------------
    -    #         (c a -- d)
    +    # Second rule.
    +    #
    +    #       (a --) (c -- d)
    +    #    ---------------------
    +    #         (c a -- d)
     
    -    elif not f_out:
    +    elif not f_out:
     
    -        fg_in, fg_out = g_in + f_in, g_out
    +        fg_in, fg_out = g_in + f_in, g_out
     
    -    else: # Unify, update, recur.
    +    else: # Unify, update, recur.
     
    -        fo, gi = f_out[-1], g_in[-1]
    +        fo, gi = f_out[-1], g_in[-1]
     
    -        s = unify(gi, fo)
    +        s = unify(gi, fo)
     
    -        if s == False:  # s can also be the empty dict, which is ok.
    -            raise TypeError('Cannot unify %r and %r.' % (fo, gi))
    +        if s == False:  # s can also be the empty dict, which is ok.
    +            raise TypeError('Cannot unify %r and %r.' % (fo, gi))
     
    -        f_g = (f_in, f_out[:-1]), (g_in[:-1], g_out)
    +        f_g = (f_in, f_out[:-1]), (g_in[:-1], g_out)
     
    -        if s: f_g = update(s, f_g)
    +        if s: f_g = update(s, f_g)
     
    -        fg_in, fg_out = compose(*f_g)
    +        fg_in, fg_out = compose(*f_g)
     
    -    return fg_in, fg_out
    +    return fg_in, fg_out
     
    -
    -
    +
    +

    unify()

    -
    def unify(u, v, s=None):
    -    if s is None:
    -        s = {}
    +
    def unify(u, v, s=None):
    +    if s is None:
    +        s = {}
     
    -    if isinstance(u, int):
    -        s[u] = v
    -    elif isinstance(v, int):
    -        s[v] = u
    -    else:
    -        s = False
    +    if isinstance(u, int):
    +        s[u] = v
    +    elif isinstance(v, int):
    +        s[v] = u
    +    else:
    +        s = False
     
    -    return s
    +    return s
     
    -
    -
    +
    +

    update()

    -
    def update(s, term):
    -    if not isinstance(term, tuple):
    -        return s.get(term, term)
    -    return tuple(update(s, inner) for inner in term)
    +
    def update(s, term):
    +    if not isinstance(term, tuple):
    +        return s.get(term, term)
    +    return tuple(update(s, inner) for inner in term)
     
    -
    -
    +
    +

    relabel()

    -
    def relabel(left, right):
    -    return left, _1000(right)
    +
    def relabel(left, right):
    +    return left, _1000(right)
     
    -def _1000(right):
    -    if not isinstance(right, tuple):
    -        return 1000 + right
    -    return tuple(_1000(n) for n in right)
    +def _1000(right):
    +    if not isinstance(right, tuple):
    +        return 1000 + right
    +    return tuple(_1000(n) for n in right)
     
    -relabel(pop, swap)
    +relabel(pop, swap)
     
    (((1,), ()), ((1001, 1002), (1002, 1001)))
     
    -
    -
    +
    +

    delabel()

    -
    def delabel(f):
    -    s = {u: i for i, u in enumerate(sorted(_unique(f)))}
    -    return update(s, f)
    +
    def delabel(f):
    +    s = {u: i for i, u in enumerate(sorted(_unique(f)))}
    +    return update(s, f)
     
    -def _unique(f, seen=None):
    -    if seen is None:
    -        seen = set()
    -    if not isinstance(f, tuple):
    -        seen.add(f)
    -    else:
    -        for inner in f:
    -            _unique(inner, seen)
    -    return seen
    +def _unique(f, seen=None):
    +    if seen is None:
    +        seen = set()
    +    if not isinstance(f, tuple):
    +        seen.add(f)
    +    else:
    +        for inner in f:
    +            _unique(inner, seen)
    +    return seen
     
    -delabel(relabel(pop, swap))
    +delabel(relabel(pop, swap))
     
    (((0,), ()), ((1, 2), (2, 1)))
     
    -
    -
    +
    +

    C()

    At last we put it all together in a function C() that accepts two stack effect comments and returns their composition (or raises and exception if they can’t be composed due to type conflicts.)

    -
    def C(f, g):
    -    f, g = relabel(f, g)
    -    fg = compose(f, g)
    -    return delabel(fg)
    +
    def C(f, g):
    +    f, g = relabel(f, g)
    +    fg = compose(f, g)
    +    return delabel(fg)
     

    Let’s try it out.

    -
    C(pop, swap)
    +
    C(pop, swap)
     
    ((1, 2, 0), (2, 1))
     
    -
    C(C(pop, swap), roll_dn)
    +
    C(C(pop, swap), roll_dn)
     
    ((3, 1, 2, 0), (2, 1, 3))
     
    -
    C(swap, roll_dn)
    +
    C(swap, roll_dn)
     
    ((2, 0, 1), (1, 0, 2))
     
    -
    C(pop, C(swap, roll_dn))
    +
    C(pop, C(swap, roll_dn))
     
    ((3, 1, 2, 0), (2, 1, 3))
     
    -
    poswrd = reduce(C, (pop, swap, roll_dn))
    -poswrd
    +
    poswrd = reduce(C, (pop, swap, roll_dn))
    +poswrd
     
    ((3, 1, 2, 0), (2, 1, 3))
     
    -
    -
    +
    +

    Stack Functions

    Here’s that trick to represent functions like rest and cons that manipulate stacks. We use a cons-list of tuples and give the tails their own numbers. Then everything above already works.

    -
    rest = ((1, 2),), (2,)
    +
    rest = ((1, 2),), (2,)
     
    -cons = (1, 2), ((1, 2),)
    +cons = (1, 2), ((1, 2),)
     
    -
    C(poswrd, rest)
    +
    C(poswrd, rest)
     
    (((3, 4), 1, 2, 0), (2, 1, 4))
    @@ -541,9 +542,9 @@ own numbers. Then everything above already works.

    }
    -
    F = reduce(C, (pop, swap, roll_dn, rest, rest, cons, cons))
    +
    F = reduce(C, (pop, swap, roll_dn, rest, rest, cons, cons))
     
    -F
    +F
     
    (((3, (4, 5)), 1, 2, 0), ((2, (1, 5)),))
    @@ -554,76 +555,76 @@ own numbers. Then everything above already works.

    3 4 5 1 2 0 2 1 5
    -
    -
    +
    +

    Dealing with cons and uncons

    However, if we try to compose e.g. cons and uncons it won’t work:

    -
    uncons = ((1, 2),), (1, 2)
    +
    uncons = ((1, 2),), (1, 2)
     
    -
    try:
    -    C(cons, uncons)
    -except Exception, e:
    -    print e
    +
    try:
    +    C(cons, uncons)
    +except Exception, e:
    +    print e
     
    Cannot unify (1, 2) and (1001, 1002).
     
    -
    +

    unify() version 2

    The problem is that the unify() function as written doesn’t handle the case when both terms are tuples. We just have to add a clause to deal with this recursively:

    -
    def unify(u, v, s=None):
    -    if s is None:
    -        s = {}
    -    elif s:
    -        u = update(s, u)
    -        v = update(s, v)
    +
    def unify(u, v, s=None):
    +    if s is None:
    +        s = {}
    +    elif s:
    +        u = update(s, u)
    +        v = update(s, v)
     
    -    if isinstance(u, int):
    -        s[u] = v
    +    if isinstance(u, int):
    +        s[u] = v
     
    -    elif isinstance(v, int):
    -        s[v] = u
    +    elif isinstance(v, int):
    +        s[v] = u
     
    -    elif isinstance(u, tuple) and isinstance(v, tuple):
    +    elif isinstance(u, tuple) and isinstance(v, tuple):
     
    -        if len(u) != 2 or len(v) != 2:
    -            # Not a type error, caller passed in a bad value.
    -            raise ValueError(repr((u, v)))  # FIXME this message sucks.
    +        if len(u) != 2 or len(v) != 2:
    +            # Not a type error, caller passed in a bad value.
    +            raise ValueError(repr((u, v)))  # FIXME this message sucks.
     
    -        (a, b), (c, d) = u, v
    -        s = unify(a, c, s)
    -        if s != False:
    -            s = unify(b, d, s)
    -    else:
    -        s = False
    +        (a, b), (c, d) = u, v
    +        s = unify(a, c, s)
    +        if s != False:
    +            s = unify(b, d, s)
    +    else:
    +        s = False
     
    -    return s
    +    return s
     
    -
    C(cons, uncons)
    +
    C(cons, uncons)
     
    ((0, 1), (0, 1))
     
    -
    -
    -
    -
    +
    +
    +
    +

    Part III: Compiling Yin Functions

    Now consider the Python function we would like to derive:

    -
    def F_python(stack):
    -    (_, (d, (c, ((a, (b, S0)), stack)))) = stack
    -    return (d, (c, S0)), stack
    +
    def F_python(stack):
    +    (_, (d, (c, ((a, (b, S0)), stack)))) = stack
    +    return (d, (c, S0)), stack
     

    And compare it to the input stack effect comment tuple we just computed:

    -
    F[0]
    +
    F[0]
     
    ((3, (4, 5)), 1, 2, 0)
    @@ -645,7 +646,7 @@ stack effect comment tuple, just in the reverse order:

    Eh?

    And the return tuple

    -
    F[1]
    +
    F[1]
     
    ((2, (1, 5)),)
    @@ -661,91 +662,91 @@ the stack effect comment tuples and returns a new Python function
     (either as a string of code or a function object ready to use) that
     performs the semantics of that Joy function (described by the stack
     effect.)

    -
    +

    Python Identifiers

    We want to substitute Python identifiers for the integers. I’m going to repurpose joy.parser.Symbol class for this:

    -
    from collections import defaultdict
    -from joy.parser import Symbol
    +
    from collections import defaultdict
    +from joy.parser import Symbol
     
     
    -def _names_for():
    -    I = iter(xrange(1000))
    -    return lambda: Symbol('a%i' % next(I))
    +def _names_for():
    +    I = iter(xrange(1000))
    +    return lambda: Symbol('a%i' % next(I))
     
     
    -def identifiers(term, s=None):
    -    if s is None:
    -        s = defaultdict(_names_for())
    -    if isinstance(term, int):
    -        return s[term]
    -    return tuple(identifiers(inner, s) for inner in term)
    +def identifiers(term, s=None):
    +    if s is None:
    +        s = defaultdict(_names_for())
    +    if isinstance(term, int):
    +        return s[term]
    +    return tuple(identifiers(inner, s) for inner in term)
     
    -
    -
    +
    +

    doc_from_stack_effect()

    As a convenience I’ve implemented a function to convert the Python stack effect comment tuples to reasonable text format. There are some details in how this code works that related to stuff later in the notebook, so you should skip it for now and read it later if you’re interested.

    -
    def doc_from_stack_effect(inputs, outputs):
    -    return '(%s--%s)' % (
    -        ' '.join(map(_to_str, inputs + ('',))),
    -        ' '.join(map(_to_str, ('',) + outputs))
    -    )
    +
    def doc_from_stack_effect(inputs, outputs):
    +    return '(%s--%s)' % (
    +        ' '.join(map(_to_str, inputs + ('',))),
    +        ' '.join(map(_to_str, ('',) + outputs))
    +    )
     
     
    -def _to_str(term):
    -    if not isinstance(term, tuple):
    -        try:
    -            t = term.prefix == 's'
    -        except AttributeError:
    -            return str(term)
    -        return '[.%i.]' % term.number if t else str(term)
    +def _to_str(term):
    +    if not isinstance(term, tuple):
    +        try:
    +            t = term.prefix == 's'
    +        except AttributeError:
    +            return str(term)
    +        return '[.%i.]' % term.number if t else str(term)
     
    -    a = []
    -    while term and isinstance(term, tuple):
    -        item, term = term
    -        a.append(_to_str(item))
    +    a = []
    +    while term and isinstance(term, tuple):
    +        item, term = term
    +        a.append(_to_str(item))
     
    -    try:
    -        n = term.number
    -    except AttributeError:
    -        n = term
    -    else:
    -        if term.prefix != 's':
    -            raise ValueError('Stack label: %s' % (term,))
    +    try:
    +        n = term.number
    +    except AttributeError:
    +        n = term
    +    else:
    +        if term.prefix != 's':
    +            raise ValueError('Stack label: %s' % (term,))
     
    -    a.append('.%s.' % (n,))
    -    return '[%s]' % ' '.join(a)
    +    a.append('.%s.' % (n,))
    +    return '[%s]' % ' '.join(a)
     
    -
    -
    +
    +

    compile_()

    Now we can write a compiler function to emit Python source code. (The underscore suffix distiguishes it from the built-in compile() function.)

    -
    def compile_(name, f, doc=None):
    -    if doc is None:
    -        doc = doc_from_stack_effect(*f)
    -    inputs, outputs = identifiers(f)
    -    i = o = Symbol('stack')
    -    for term in inputs:
    -        i = term, i
    -    for term in outputs:
    -        o = term, o
    -    return '''def %s(stack):
    -    """%s"""
    -    %s = stack
    -    return %s''' % (name, doc, i, o)
    +
    def compile_(name, f, doc=None):
    +    if doc is None:
    +        doc = doc_from_stack_effect(*f)
    +    inputs, outputs = identifiers(f)
    +    i = o = Symbol('stack')
    +    for term in inputs:
    +        i = term, i
    +    for term in outputs:
    +        o = term, o
    +    return '''def %s(stack):
    +    """%s"""
    +    %s = stack
    +    return %s''' % (name, doc, i, o)
     

    Here it is in action:

    -
    source = compile_('F', F)
    +
    source = compile_('F', F)
     
    -print source
    +print source
     
    def F(stack):
    @@ -755,31 +756,31 @@ function.)

    Compare:

    -
    def F_python(stack):
    -    (_, (d, (c, ((a, (b, S0)), stack)))) = stack
    -    return ((d, (c, S0)), stack)
    +
    def F_python(stack):
    +    (_, (d, (c, ((a, (b, S0)), stack)))) = stack
    +    return ((d, (c, S0)), stack)
     

    Next steps:

    -
    L = {}
    +
    L = {}
     
    -eval(compile(source, '__main__', 'single'), {}, L)
    +eval(compile(source, '__main__', 'single'), {}, L)
     
    -L['F']
    +L['F']
     
    <function F>
     

    Let’s try it out:

    -
    from notebook_preamble import D, J, V
    -from joy.library import SimpleFunctionWrapper
    +
    from notebook_preamble import D, J, V
    +from joy.library import SimpleFunctionWrapper
     
    -
    D['F'] = SimpleFunctionWrapper(L['F'])
    +
    D['F'] = SimpleFunctionWrapper(L['F'])
     
    -
    J('[4 5 ...] 2 3 1 F')
    +
    J('[4 5 ...] 2 3 1 F')
     
    [3 2 ...]
    @@ -794,38 +795,38 @@ automatically. It might be a reasonable idea to detect sequences of
     compilable functions in definitions that have uncompilable functions in
     them and just compile those. However, if your library is well-factored
     this might be less helpful.

    -
    -
    +
    +

    Compiling Library Functions

    We can use compile_() to generate many primitives in the library from their stack effect comments:

    -
    def defs():
    +
    def defs():
     
    -    rolldown = (1, 2, 3), (2, 3, 1)
    +    rolldown = (1, 2, 3), (2, 3, 1)
     
    -    rollup = (1, 2, 3), (3, 1, 2)
    +    rollup = (1, 2, 3), (3, 1, 2)
     
    -    pop = (1,), ()
    +    pop = (1,), ()
     
    -    swap = (1, 2), (2, 1)
    +    swap = (1, 2), (2, 1)
     
    -    rest = ((1, 2),), (2,)
    +    rest = ((1, 2),), (2,)
     
    -    rrest = C(rest, rest)
    +    rrest = C(rest, rest)
     
    -    cons = (1, 2), ((1, 2),)
    +    cons = (1, 2), ((1, 2),)
     
    -    uncons = ((1, 2),), (1, 2)
    +    uncons = ((1, 2),), (1, 2)
     
    -    swons = C(swap, cons)
    +    swons = C(swap, cons)
     
    -    return locals()
    +    return locals()
     
    -
    for name, stack_effect_comment in sorted(defs().items()):
    -    print
    -    print compile_(name, stack_effect_comment)
    -    print
    +
    for name, stack_effect_comment in sorted(defs().items()):
    +    print
    +    print compile_(name, stack_effect_comment)
    +    print
     
    def cons(stack):
    @@ -882,14 +883,14 @@ from their stack effect comments:

    return (a1, (a0, stack))
    -
    -
    -
    +
    +
    +

    Part IV: Types and Subtypes of Arguments

    So far we have dealt with types of functions, those dealing with simple stack manipulation. Let’s extend our machinery to deal with types of arguments.

    -
    +

    “Number” Type

    Consider the definition of sqr:

    sqr == dup mul
    @@ -899,8 +900,8 @@ arguments.

    dup (1 -- 1 1)
     
    -

    And mul accepts two “numbers” (we’re ignoring ints vs. floats -vs. complex, etc., for now) and returns just one:

    +

    And mul accepts two “numbers” (we’re ignoring ints vs. floats +vs. complex, etc., for now) and returns just one:

    mul (n n -- n)
     
    @@ -924,8 +925,8 @@ the case:

    The important thing here is that the mapping is going the same way in both cases, from the “any” integer to the number

    -
    -
    +
    +

    Distinguishing Numbers

    We should also mind that the number that mul produces is not (necessarily) the same as either of its inputs, which are not @@ -943,8 +944,8 @@ both cases, from the “any” integer to the number

    (n2 n1 -- )∘( -- n3 n3)
    - -
    + +

    Distinguishing Types

    So we need separate domains of “any” numbers and “number” numbers, and we need to be able to ask the order of these domains. Now the notes on @@ -965,57 +966,57 @@ and t Python class hierarchy of Joy types and use the issubclass() method to establish domain ordering, as well as other handy behaviour that will make it fairly easy to reuse most of the code above.

    -
    class AnyJoyType(object):
    +
    class AnyJoyType(object):
     
    -    prefix = 'a'
    +    prefix = 'a'
     
    -    def __init__(self, number):
    -        self.number = number
    +    def __init__(self, number):
    +        self.number = number
     
    -    def __repr__(self):
    -        return self.prefix + str(self.number)
    +    def __repr__(self):
    +        return self.prefix + str(self.number)
     
    -    def __eq__(self, other):
    -        return (
    -            isinstance(other, self.__class__)
    -            and other.prefix == self.prefix
    -            and other.number == self.number
    -        )
    +    def __eq__(self, other):
    +        return (
    +            isinstance(other, self.__class__)
    +            and other.prefix == self.prefix
    +            and other.number == self.number
    +        )
     
    -    def __ge__(self, other):
    -        return issubclass(other.__class__, self.__class__)
    +    def __ge__(self, other):
    +        return issubclass(other.__class__, self.__class__)
     
    -    def __add__(self, other):
    -        return self.__class__(self.number + other)
    -    __radd__ = __add__
    +    def __add__(self, other):
    +        return self.__class__(self.number + other)
    +    __radd__ = __add__
     
    -    def __hash__(self):
    -        return hash(repr(self))
    +    def __hash__(self):
    +        return hash(repr(self))
     
     
    -class NumberJoyType(AnyJoyType): prefix = 'n'
    -class FloatJoyType(NumberJoyType): prefix = 'f'
    -class IntJoyType(FloatJoyType): prefix = 'i'
    +class NumberJoyType(AnyJoyType): prefix = 'n'
    +class FloatJoyType(NumberJoyType): prefix = 'f'
    +class IntJoyType(FloatJoyType): prefix = 'i'
     
     
    -class StackJoyType(AnyJoyType):
    -    prefix = 's'
    +class StackJoyType(AnyJoyType):
    +    prefix = 's'
     
     
    -_R = range(10)
    -A = map(AnyJoyType, _R)
    -N = map(NumberJoyType, _R)
    -S = map(StackJoyType, _R)
    +_R = range(10)
    +A = map(AnyJoyType, _R)
    +N = map(NumberJoyType, _R)
    +S = map(StackJoyType, _R)
     

    Mess with it a little:

    -
    from itertools import permutations
    +
    from itertools import permutations
     

    “Any” types can be specialized to numbers and stacks, but not vice versa:

    -
    for a, b in permutations((A[0], N[0], S[0]), 2):
    -    print a, '>=', b, '->', a >= b
    +
    for a, b in permutations((A[0], N[0], S[0]), 2):
    +    print a, '>=', b, '->', a >= b
     
    a0 >= n0 -> True
    @@ -1029,8 +1030,8 @@ versa:

    Our crude Numerical Tower of numbers > floats > integers works as well (but we’re not going to use it yet):

    -
    for a, b in permutations((A[0], N[0], FloatJoyType(0), IntJoyType(0)), 2):
    -    print a, '>=', b, '->', a >= b
    +
    for a, b in permutations((A[0], N[0], FloatJoyType(0), IntJoyType(0)), 2):
    +    print a, '>=', b, '->', a >= b
     
    a0 >= n0 -> True
    @@ -1047,178 +1048,178 @@ Tower of numbers >
     i0 >= f0 -> False
     
    -
    -
    +
    +

    Typing sqr

    -
    dup = (A[1],), (A[1], A[1])
    +
    dup = (A[1],), (A[1], A[1])
     
    -mul = (N[1], N[2]), (N[3],)
    +mul = (N[1], N[2]), (N[3],)
     
    -
    dup
    +
    dup
     
    ((a1,), (a1, a1))
     
    -
    mul
    +
    mul
     
    ((n1, n2), (n3,))
     
    -
    -
    +
    +

    Modifying the Inferencer

    Re-labeling still works fine:

    -
    foo = relabel(dup, mul)
    +
    foo = relabel(dup, mul)
     
    -foo
    +foo
     
    (((a1,), (a1, a1)), ((n1001, n1002), (n1003,)))
     
    -
    +

    delabel() version 2

    The delabel() function needs an overhaul. It now has to keep track of how many labels of each domain it has “seen”.

    -
    from collections import Counter
    +
    from collections import Counter
     
     
    -def delabel(f, seen=None, c=None):
    -    if seen is None:
    -        assert c is None
    -        seen, c = {}, Counter()
    +def delabel(f, seen=None, c=None):
    +    if seen is None:
    +        assert c is None
    +        seen, c = {}, Counter()
     
    -    try:
    -        return seen[f]
    -    except KeyError:
    -        pass
    +    try:
    +        return seen[f]
    +    except KeyError:
    +        pass
     
    -    if not isinstance(f, tuple):
    -        seen[f] = f.__class__(c[f.prefix] + 1)
    -        c[f.prefix] += 1
    -        return seen[f]
    +    if not isinstance(f, tuple):
    +        seen[f] = f.__class__(c[f.prefix] + 1)
    +        c[f.prefix] += 1
    +        return seen[f]
     
    -    return tuple(delabel(inner, seen, c) for inner in f)
    +    return tuple(delabel(inner, seen, c) for inner in f)
     
    -
    delabel(foo)
    +
    delabel(foo)
     
    (((a1,), (a1, a1)), ((n1, n2), (n3,)))
     
    -
    -
    +
    +

    unify() version 3

    -
    def unify(u, v, s=None):
    -    if s is None:
    -        s = {}
    -    elif s:
    -        u = update(s, u)
    -        v = update(s, v)
    +
    def unify(u, v, s=None):
    +    if s is None:
    +        s = {}
    +    elif s:
    +        u = update(s, u)
    +        v = update(s, v)
     
    -    if u == v:
    -        return s
    +    if u == v:
    +        return s
     
    -    if isinstance(u, AnyJoyType) and isinstance(v, AnyJoyType):
    -        if u >= v:
    -            s[u] = v
    -            return s
    -        if v >= u:
    -            s[v] = u
    -            return s
    -        raise TypeError('Cannot unify %r and %r.' % (u, v))
    +    if isinstance(u, AnyJoyType) and isinstance(v, AnyJoyType):
    +        if u >= v:
    +            s[u] = v
    +            return s
    +        if v >= u:
    +            s[v] = u
    +            return s
    +        raise TypeError('Cannot unify %r and %r.' % (u, v))
     
    -    if isinstance(u, tuple) and isinstance(v, tuple):
    -        if len(u) != len(v) != 2:
    -            raise TypeError(repr((u, v)))
    -        for uu, vv in zip(u, v):
    -            s = unify(uu, vv, s)
    -            if s == False: # (instead of a substitution dict.)
    -                break
    -        return s
    +    if isinstance(u, tuple) and isinstance(v, tuple):
    +        if len(u) != len(v) != 2:
    +            raise TypeError(repr((u, v)))
    +        for uu, vv in zip(u, v):
    +            s = unify(uu, vv, s)
    +            if s == False: # (instead of a substitution dict.)
    +                break
    +        return s
     
    -    if isinstance(v, tuple):
    -        if not stacky(u):
    -            raise TypeError('Cannot unify %r and %r.' % (u, v))
    -        s[u] = v
    -        return s
    +    if isinstance(v, tuple):
    +        if not stacky(u):
    +            raise TypeError('Cannot unify %r and %r.' % (u, v))
    +        s[u] = v
    +        return s
     
    -    if isinstance(u, tuple):
    -        if not stacky(v):
    -            raise TypeError('Cannot unify %r and %r.' % (v, u))
    -        s[v] = u
    -        return s
    +    if isinstance(u, tuple):
    +        if not stacky(v):
    +            raise TypeError('Cannot unify %r and %r.' % (v, u))
    +        s[v] = u
    +        return s
     
    -    return False
    +    return False
     
     
    -def stacky(thing):
    -    return thing.__class__ in {AnyJoyType, StackJoyType}
    +def stacky(thing):
    +    return thing.__class__ in {AnyJoyType, StackJoyType}
     

    Rewrite the stack effect comments:

    -
    def defs():
    +
    def defs():
     
    -    rolldown = (A[1], A[2], A[3]), (A[2], A[3], A[1])
    +    rolldown = (A[1], A[2], A[3]), (A[2], A[3], A[1])
     
    -    rollup = (A[1], A[2], A[3]), (A[3], A[1], A[2])
    +    rollup = (A[1], A[2], A[3]), (A[3], A[1], A[2])
     
    -    pop = (A[1],), ()
    +    pop = (A[1],), ()
     
    -    popop = (A[2], A[1],), ()
    +    popop = (A[2], A[1],), ()
     
    -    popd = (A[2], A[1],), (A[1],)
    +    popd = (A[2], A[1],), (A[1],)
     
    -    popdd = (A[3], A[2], A[1],), (A[2], A[1],)
    +    popdd = (A[3], A[2], A[1],), (A[2], A[1],)
     
    -    swap = (A[1], A[2]), (A[2], A[1])
    +    swap = (A[1], A[2]), (A[2], A[1])
     
    -    rest = ((A[1], S[1]),), (S[1],)
    +    rest = ((A[1], S[1]),), (S[1],)
     
    -    rrest = C(rest, rest)
    +    rrest = C(rest, rest)
     
    -    cons = (A[1], S[1]), ((A[1], S[1]),)
    +    cons = (A[1], S[1]), ((A[1], S[1]),)
     
    -    ccons = C(cons, cons)
    +    ccons = C(cons, cons)
     
    -    uncons = ((A[1], S[1]),), (A[1], S[1])
    +    uncons = ((A[1], S[1]),), (A[1], S[1])
     
    -    swons = C(swap, cons)
    +    swons = C(swap, cons)
     
    -    dup = (A[1],), (A[1], A[1])
    +    dup = (A[1],), (A[1], A[1])
     
    -    dupd = (A[2], A[1]), (A[2], A[2], A[1])
    +    dupd = (A[2], A[1]), (A[2], A[2], A[1])
     
    -    mul = (N[1], N[2]), (N[3],)
    +    mul = (N[1], N[2]), (N[3],)
     
    -    sqrt = C(dup, mul)
    +    sqrt = C(dup, mul)
     
    -    first = ((A[1], S[1]),), (A[1],)
    +    first = ((A[1], S[1]),), (A[1],)
     
    -    second = C(rest, first)
    +    second = C(rest, first)
     
    -    third = C(rest, second)
    +    third = C(rest, second)
     
    -    tuck = (A[2], A[1]), (A[1], A[2], A[1])
    +    tuck = (A[2], A[1]), (A[1], A[2], A[1])
     
    -    over = (A[2], A[1]), (A[2], A[1], A[2])
    +    over = (A[2], A[1]), (A[2], A[1], A[2])
     
    -    succ = pred = (N[1],), (N[2],)
    +    succ = pred = (N[1],), (N[2],)
     
    -    divmod_ = pm = (N[2], N[1]), (N[4], N[3])
    +    divmod_ = pm = (N[2], N[1]), (N[4], N[3])
     
    -    return locals()
    +    return locals()
     
    -
    DEFS = defs()
    +
    DEFS = defs()
     
    -
    for name, stack_effect_comment in sorted(DEFS.items()):
    -    print name, '=', doc_from_stack_effect(*stack_effect_comment)
    +
    for name, stack_effect_comment in sorted(DEFS.items()):
    +    print name, '=', doc_from_stack_effect(*stack_effect_comment)
     
    ccons = (a1 a2 [.1.] -- [a1 a2 .1.])
    @@ -1249,27 +1250,27 @@ of how many labels of each domain it has “seen”.

    uncons = ([a1 .1.] -- a1 [.1.])
    -
    globals().update(DEFS)
    +
    globals().update(DEFS)
     
    -
    -
    +
    +

    Compose dup and mul

    -
    C(dup, mul)
    +
    C(dup, mul)
     
    ((n1,), (n2,))
     

    Revisit the F function, works fine.

    -
    F = reduce(C, (pop, swap, rolldown, rest, rest, cons, cons))
    -F
    +
    F = reduce(C, (pop, swap, rolldown, rest, rest, cons, cons))
    +F
     
    (((a1, (a2, s1)), a3, a4, a5), ((a4, (a3, s1)),))
     
    -
    print doc_from_stack_effect(*F)
    +
    print doc_from_stack_effect(*F)
     
    ([a1 a2 .1.] a3 a4 a5 -- [a4 a3 .1.])
    @@ -1277,52 +1278,52 @@ of how many labels of each domain it has “seen”.

    Some otherwise inefficient functions are no longer to be feared. We can also get the effect of combinators in some limited cases.

    -
    def neato(*funcs):
    -    print doc_from_stack_effect(*reduce(C, funcs))
    +
    def neato(*funcs):
    +    print doc_from_stack_effect(*reduce(C, funcs))
     
    -
    # e.g. [swap] dip
    -neato(rollup, swap, rolldown)
    +
    # e.g. [swap] dip
    +neato(rollup, swap, rolldown)
     
    (a1 a2 a3 -- a2 a1 a3)
     
    -
    # e.g. [popop] dipd
    -neato(popdd, rolldown, pop)
    +
    # e.g. [popop] dipd
    +neato(popdd, rolldown, pop)
     
    (a1 a2 a3 a4 -- a3 a4)
     
    -
    # Reverse the order of the top three items.
    -neato(rollup, swap)
    +
    # Reverse the order of the top three items.
    +neato(rollup, swap)
     
    (a1 a2 a3 -- a3 a2 a1)
     
    -
    -
    +
    +

    compile_() version 2

    Because the type labels represent themselves as valid Python identifiers the compile_() function doesn’t need to generate them anymore:

    -
    def compile_(name, f, doc=None):
    -    inputs, outputs = f
    -    if doc is None:
    -        doc = doc_from_stack_effect(inputs, outputs)
    -    i = o = Symbol('stack')
    -    for term in inputs:
    -        i = term, i
    -    for term in outputs:
    -        o = term, o
    -    return '''def %s(stack):
    -    """%s"""
    -    %s = stack
    -    return %s''' % (name, doc, i, o)
    +
    def compile_(name, f, doc=None):
    +    inputs, outputs = f
    +    if doc is None:
    +        doc = doc_from_stack_effect(inputs, outputs)
    +    i = o = Symbol('stack')
    +    for term in inputs:
    +        i = term, i
    +    for term in outputs:
    +        o = term, o
    +    return '''def %s(stack):
    +    """%s"""
    +    %s = stack
    +    return %s''' % (name, doc, i, o)
     
    -
    print compile_('F', F)
    +
    print compile_('F', F)
     
    def F(stack):
    @@ -1331,9 +1332,9 @@ the compile_()return ((a4, (a3, s1)), stack)
     
    -

    But it cannot magically create new functions that involve e.g. math and +

    But it cannot magically create new functions that involve e.g. math and such. Note that this is not a sqr function implementation:

    -
    print compile_('sqr', C(dup, mul))
    +
    print compile_('sqr', C(dup, mul))
     
    def sqr(stack):
    @@ -1349,22 +1350,22 @@ insert it in the right place. 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:

    -
    from itertools import imap
    +
    from itertools import imap
     
     
    -def compilable(f):
    -    return isinstance(f, tuple) and all(imap(compilable, f)) or stacky(f)
    +def compilable(f):
    +    return isinstance(f, tuple) and all(imap(compilable, f)) or stacky(f)
     
    -
    for name, stack_effect_comment in sorted(defs().items()):
    -    if compilable(stack_effect_comment):
    -        print name, '=', doc_from_stack_effect(*stack_effect_comment)
    +
    for name, stack_effect_comment in sorted(defs().items()):
    +    if compilable(stack_effect_comment):
    +        print name, '=', doc_from_stack_effect(*stack_effect_comment)
     
    ccons = (a1 a2 [.1.] -- [a1 a2 .1.])
    @@ -1389,10 +1390,10 @@ comments. We can write a function to check that:

    uncons = ([a1 .1.] -- a1 [.1.])
    -
    -
    -
    -
    +
    +
    + +

    Part V: Functions that use the Stack

    Consider the stack function which grabs the whole stack, quotes it, and puts it on itself:

    @@ -1411,7 +1412,7 @@ simple, elegant trick.

    Instead of representing the stack effect comments as a single tuple (with N items in it) we use the same cons-list structure to hold the sequence and unify() the whole comments.

    -
    +

    stack∘uncons

    Let’s try composing stack and uncons. We want this result:

    stack∘uncons (... a -- ... a a [...])
    @@ -1434,8 +1435,8 @@ sequence and unify(
     

    It works.

    -
    -
    +
    +

    stack∘uncons∘uncons

    Let’s try stack∘uncons∘uncons:

    (a, S     ) -- (S,      (a, (a, S     ))) ∘ ((b, Z),  S`             ) -- (Z, (b,   S`   ))
    @@ -1452,7 +1453,7 @@ sequence and unify(
     

    It works.

    -
    +

    compose() version 2

    This function has to be modified to use the new datastructures and it is no longer recursive, instead recursion happens as part of unification. @@ -1464,96 +1465,96 @@ first two rules’ the “truthiness” of StackJoyType to false to let e.g. joy.utils.stack.concat work with our stack effect comment cons-list tuples.)

    -
    def compose(f, g):
    -    (f_in, f_out), (g_in, g_out) = f, g
    -    s = unify(g_in, f_out)
    -    if s == False:  # s can also be the empty dict, which is ok.
    -        raise TypeError('Cannot unify %r and %r.' % (f_out, g_in))
    -    return update(s, (f_in, g_out))
    +
    def compose(f, g):
    +    (f_in, f_out), (g_in, g_out) = f, g
    +    s = unify(g_in, f_out)
    +    if s == False:  # s can also be the empty dict, which is ok.
    +        raise TypeError('Cannot unify %r and %r.' % (f_out, g_in))
    +    return update(s, (f_in, g_out))
     

    I don’t want to rewrite all the defs myself, so I’ll write a little conversion function instead. This is programmer’s laziness.

    -
    def sequence_to_stack(seq, stack=StackJoyType(23)):
    -    for item in seq: stack = item, stack
    -    return stack
    +
    def sequence_to_stack(seq, stack=StackJoyType(23)):
    +    for item in seq: stack = item, stack
    +    return stack
     
    -NEW_DEFS = {
    -    name: (sequence_to_stack(i), sequence_to_stack(o))
    -    for name, (i, o) in DEFS.iteritems()
    -}
    -NEW_DEFS['stack'] = S[0], (S[0], S[0])
    -NEW_DEFS['swaack'] = (S[1], S[0]), (S[0], S[1])
    -globals().update(NEW_DEFS)
    +NEW_DEFS = {
    +    name: (sequence_to_stack(i), sequence_to_stack(o))
    +    for name, (i, o) in DEFS.iteritems()
    +}
    +NEW_DEFS['stack'] = S[0], (S[0], S[0])
    +NEW_DEFS['swaack'] = (S[1], S[0]), (S[0], S[1])
    +globals().update(NEW_DEFS)
     
    -
    C(stack, uncons)
    +
    C(stack, uncons)
     
    ((a1, s1), (s1, (a1, (a1, s1))))
     
    -
    reduce(C, (stack, uncons, uncons))
    +
    reduce(C, (stack, uncons, uncons))
     
    ((a1, (a2, s1)), (s1, (a2, (a1, (a1, (a2, s1))))))
     

    The display function should be changed too.

    -
    -
    -
    +
    +
    +

    doc_from_stack_effect() version 2

    Clunky junk, but it will suffice for now.

    -
    def doc_from_stack_effect(inputs, outputs):
    -    switch = [False]  # Do we need to display the '...' for the rest of the main stack?
    -    i, o = _f(inputs, switch), _f(outputs, switch)
    -    if switch[0]:
    -        i.append('...')
    -        o.append('...')
    -    return '(%s--%s)' % (
    -        ' '.join(reversed([''] + i)),
    -        ' '.join(reversed(o + [''])),
    -    )
    +
    def doc_from_stack_effect(inputs, outputs):
    +    switch = [False]  # Do we need to display the '...' for the rest of the main stack?
    +    i, o = _f(inputs, switch), _f(outputs, switch)
    +    if switch[0]:
    +        i.append('...')
    +        o.append('...')
    +    return '(%s--%s)' % (
    +        ' '.join(reversed([''] + i)),
    +        ' '.join(reversed(o + [''])),
    +    )
     
     
    -def _f(term, switch):
    -    a = []
    -    while term and isinstance(term, tuple):
    -        item, term = term
    -        a.append(item)
    -    assert isinstance(term, StackJoyType), repr(term)
    -    a = [_to_str(i, term, switch) for i in a]
    -    return a
    +def _f(term, switch):
    +    a = []
    +    while term and isinstance(term, tuple):
    +        item, term = term
    +        a.append(item)
    +    assert isinstance(term, StackJoyType), repr(term)
    +    a = [_to_str(i, term, switch) for i in a]
    +    return a
     
     
    -def _to_str(term, stack, switch):
    -    if not isinstance(term, tuple):
    -        if term == stack:
    -            switch[0] = True
    -            return '[...]'
    -        return (
    -            '[.%i.]' % term.number
    -            if isinstance(term, StackJoyType)
    -            else str(term)
    -        )
    +def _to_str(term, stack, switch):
    +    if not isinstance(term, tuple):
    +        if term == stack:
    +            switch[0] = True
    +            return '[...]'
    +        return (
    +            '[.%i.]' % term.number
    +            if isinstance(term, StackJoyType)
    +            else str(term)
    +        )
     
    -    a = []
    -    while term and isinstance(term, tuple):
    -        item, term = term
    -        a.append(_to_str(item, stack, switch))
    -    assert isinstance(term, StackJoyType), repr(term)
    -    if term == stack:
    -        switch[0] = True
    -        end = '...'
    -    else:
    -        end = '.%i.' % term.number
    -    a.append(end)
    -    return '[%s]' % ' '.join(a)
    +    a = []
    +    while term and isinstance(term, tuple):
    +        item, term = term
    +        a.append(_to_str(item, stack, switch))
    +    assert isinstance(term, StackJoyType), repr(term)
    +    if term == stack:
    +        switch[0] = True
    +        end = '...'
    +    else:
    +        end = '.%i.' % term.number
    +    a.append(end)
    +    return '[%s]' % ' '.join(a)
     
    -
    for name, stack_effect_comment in sorted(NEW_DEFS.items()):
    -    print name, '=', doc_from_stack_effect(*stack_effect_comment)
    +
    for name, stack_effect_comment in sorted(NEW_DEFS.items()):
    +    print name, '=', doc_from_stack_effect(*stack_effect_comment)
     
    ccons = (a1 a2 [.1.] -- [a1 a2 .1.])
    @@ -1586,10 +1587,10 @@ conversion function instead. This is programmer’s laziness.

    uncons = ([a1 .1.] -- a1 [.1.])
    -
    print ; print doc_from_stack_effect(*stack)
    -print ; print doc_from_stack_effect(*C(stack, uncons))
    -print ; print doc_from_stack_effect(*reduce(C, (stack, uncons, uncons)))
    -print ; print doc_from_stack_effect(*reduce(C, (stack, uncons, cons)))
    +
    print ; print doc_from_stack_effect(*stack)
    +print ; print doc_from_stack_effect(*C(stack, uncons))
    +print ; print doc_from_stack_effect(*reduce(C, (stack, uncons, uncons)))
    +print ; print doc_from_stack_effect(*reduce(C, (stack, uncons, cons)))
     
    (... -- ... [...])
    @@ -1601,35 +1602,35 @@ conversion function instead. This is programmer’s laziness.

    (... a1 -- ... a1 [a1 ...])
    -
    print doc_from_stack_effect(*C(ccons, stack))
    +
    print doc_from_stack_effect(*C(ccons, stack))
     
    (... a2 a1 [.1.] -- ... [a2 a1 .1.] [[a2 a1 .1.] ...])
     
    -
    Q = C(ccons, stack)
    +
    Q = C(ccons, stack)
     
    -Q
    +Q
     
    ((s1, (a1, (a2, s2))), (((a2, (a1, s1)), s2), ((a2, (a1, s1)), s2)))
     
    -
    +

    compile_() version 3

    This makes the compile_() function pretty simple as the stack effect comments are now already in the form needed for the Python code:

    -
    def compile_(name, f, doc=None):
    -    i, o = f
    -    if doc is None:
    -        doc = doc_from_stack_effect(i, o)
    -    return '''def %s(stack):
    -    """%s"""
    -    %s = stack
    -    return %s''' % (name, doc, i, o)
    +
    def compile_(name, f, doc=None):
    +    i, o = f
    +    if doc is None:
    +        doc = doc_from_stack_effect(i, o)
    +    return '''def %s(stack):
    +    """%s"""
    +    %s = stack
    +    return %s''' % (name, doc, i, o)
     
    -
    print compile_('Q', Q)
    +
    print compile_('Q', Q)
     
    def Q(stack):
    @@ -1638,63 +1639,63 @@ comments are now already in the form needed for the Python code:

    return (((a2, (a1, s1)), s2), ((a2, (a1, s1)), s2))
    -
    unstack = (S[1], S[0]), S[1]
    -enstacken = S[0], (S[0], S[1])
    +
    unstack = (S[1], S[0]), S[1]
    +enstacken = S[0], (S[0], S[1])
     
    -
    print doc_from_stack_effect(*unstack)
    +
    print doc_from_stack_effect(*unstack)
     
    ([.1.] --)
     
    -
    print doc_from_stack_effect(*enstacken)
    +
    print doc_from_stack_effect(*enstacken)
     
    (-- [.0.])
     
    -
    print doc_from_stack_effect(*C(cons, unstack))
    +
    print doc_from_stack_effect(*C(cons, unstack))
     
    (a1 [.1.] -- a1)
     
    -
    print doc_from_stack_effect(*C(cons, enstacken))
    +
    print doc_from_stack_effect(*C(cons, enstacken))
     
    (a1 [.1.] -- [[a1 .1.] .2.])
     
    -
    C(cons, unstack)
    +
    C(cons, unstack)
     
    ((s1, (a1, s2)), (a1, s1))
     
    -
    -
    -
    -
    +
    +
    + +

    Part VI: Multiple Stack Effects

    -
    class IntJoyType(NumberJoyType): prefix = 'i'
    +
    class IntJoyType(NumberJoyType): prefix = 'i'
     
     
    -F = map(FloatJoyType, _R)
    -I = map(IntJoyType, _R)
    +F = map(FloatJoyType, _R)
    +I = map(IntJoyType, _R)
     
    -
    muls = [
    -     ((I[2], (I[1], S[0])), (I[3], S[0])),
    -     ((F[2], (I[1], S[0])), (F[3], S[0])),
    -     ((I[2], (F[1], S[0])), (F[3], S[0])),
    -     ((F[2], (F[1], S[0])), (F[3], S[0])),
    -]
    +
    muls = [
    +     ((I[2], (I[1], S[0])), (I[3], S[0])),
    +     ((F[2], (I[1], S[0])), (F[3], S[0])),
    +     ((I[2], (F[1], S[0])), (F[3], S[0])),
    +     ((F[2], (F[1], S[0])), (F[3], S[0])),
    +]
     
    -
    for f in muls:
    -    print doc_from_stack_effect(*f)
    +
    for f in muls:
    +    print doc_from_stack_effect(*f)
     
    (i1 i2 -- i3)
    @@ -1703,49 +1704,49 @@ comments are now already in the form needed for the Python code:

    (f1 f2 -- f3)
    -
    for f in muls:
    -    try:
    -        e = C(dup, f)
    -    except TypeError:
    -        continue
    -    print doc_from_stack_effect(*dup), doc_from_stack_effect(*f), doc_from_stack_effect(*e)
    +
    for f in muls:
    +    try:
    +        e = C(dup, f)
    +    except TypeError:
    +        continue
    +    print doc_from_stack_effect(*dup), doc_from_stack_effect(*f), doc_from_stack_effect(*e)
     
    (a1 -- a1 a1) (i1 i2 -- i3) (i1 -- i2)
     (a1 -- a1 a1) (f1 f2 -- f3) (f1 -- f2)
     
    -
    from itertools import product
    +
    from itertools import product
     
     
    -def meta_compose(F, G):
    -    for f, g in product(F, G):
    -        try:
    -            yield C(f, g)
    -        except TypeError:
    -            pass
    +def meta_compose(F, G):
    +    for f, g in product(F, G):
    +        try:
    +            yield C(f, g)
    +        except TypeError:
    +            pass
     
     
    -def MC(F, G):
    -    return sorted(set(meta_compose(F, G)))
    +def MC(F, G):
    +    return sorted(set(meta_compose(F, G)))
     
    -
    for f in MC([dup], [mul]):
    -    print doc_from_stack_effect(*f)
    +
    for f in MC([dup], [mul]):
    +    print doc_from_stack_effect(*f)
     
    (n1 -- n2)
     
    -
    for f in MC([dup], muls):
    -    print doc_from_stack_effect(*f)
    +
    for f in MC([dup], muls):
    +    print doc_from_stack_effect(*f)
     
    (f1 -- f2)
     (i1 -- i2)
     
    -
    +

    Representing an Unbounded Sequence of Types

    We can borrow a trick from Brzozowski’s Derivatives of Regular Expressions to @@ -1792,148 +1793,148 @@ disappears:

    {c: a, d: e, .1.: A* b .0.}
    -
    class KleeneStar(object):
    +
    class KleeneStar(object):
     
    -    kind = AnyJoyType
    +    kind = AnyJoyType
     
    -    def __init__(self, number):
    -        self.number = number
    -        self.count = 0
    -        self.prefix = repr(self)
    +    def __init__(self, number):
    +        self.number = number
    +        self.count = 0
    +        self.prefix = repr(self)
     
    -    def __repr__(self):
    -        return '%s%i*' % (self.kind.prefix, self.number)
    +    def __repr__(self):
    +        return '%s%i*' % (self.kind.prefix, self.number)
     
    -    def another(self):
    -        self.count += 1
    -        return self.kind(10000 * self.number + self.count)
    +    def another(self):
    +        self.count += 1
    +        return self.kind(10000 * self.number + self.count)
     
    -    def __eq__(self, other):
    -        return (
    -            isinstance(other, self.__class__)
    -            and other.number == self.number
    -        )
    +    def __eq__(self, other):
    +        return (
    +            isinstance(other, self.__class__)
    +            and other.number == self.number
    +        )
     
    -    def __ge__(self, other):
    -        return self.kind >= other.kind
    +    def __ge__(self, other):
    +        return self.kind >= other.kind
     
    -    def __add__(self, other):
    -        return self.__class__(self.number + other)
    -    __radd__ = __add__
    +    def __add__(self, other):
    +        return self.__class__(self.number + other)
    +    __radd__ = __add__
     
    -    def __hash__(self):
    -        return hash(repr(self))
    +    def __hash__(self):
    +        return hash(repr(self))
     
    -class AnyStarJoyType(KleeneStar): kind = AnyJoyType
    -class NumberStarJoyType(KleeneStar): kind = NumberJoyType
    -#class FloatStarJoyType(KleeneStar): kind = FloatJoyType
    -#class IntStarJoyType(KleeneStar): kind = IntJoyType
    -class StackStarJoyType(KleeneStar): kind = StackJoyType
    +class AnyStarJoyType(KleeneStar): kind = AnyJoyType
    +class NumberStarJoyType(KleeneStar): kind = NumberJoyType
    +#class FloatStarJoyType(KleeneStar): kind = FloatJoyType
    +#class IntStarJoyType(KleeneStar): kind = IntJoyType
    +class StackStarJoyType(KleeneStar): kind = StackJoyType
     
     
    -As = map(AnyStarJoyType, _R)
    -Ns = map(NumberStarJoyType, _R)
    -Ss = map(StackStarJoyType, _R)
    +As = map(AnyStarJoyType, _R)
    +Ns = map(NumberStarJoyType, _R)
    +Ss = map(StackStarJoyType, _R)
     
    -
    +

    unify() version 4

    Can now return multiple results…

    -
    def unify(u, v, s=None):
    -    if s is None:
    -        s = {}
    -    elif s:
    -        u = update(s, u)
    -        v = update(s, v)
    +
    def unify(u, v, s=None):
    +    if s is None:
    +        s = {}
    +    elif s:
    +        u = update(s, u)
    +        v = update(s, v)
     
    -    if u == v:
    -        return s,
    +    if u == v:
    +        return s,
     
    -    if isinstance(u, AnyJoyType) and isinstance(v, AnyJoyType):
    -        if u >= v:
    -            s[u] = v
    -            return s,
    -        if v >= u:
    -            s[v] = u
    -            return s,
    -        raise TypeError('Cannot unify %r and %r.' % (u, v))
    +    if isinstance(u, AnyJoyType) and isinstance(v, AnyJoyType):
    +        if u >= v:
    +            s[u] = v
    +            return s,
    +        if v >= u:
    +            s[v] = u
    +            return s,
    +        raise TypeError('Cannot unify %r and %r.' % (u, v))
     
    -    if isinstance(u, tuple) and isinstance(v, tuple):
    -        if len(u) != len(v) != 2:
    -            raise TypeError(repr((u, v)))
    +    if isinstance(u, tuple) and isinstance(v, tuple):
    +        if len(u) != len(v) != 2:
    +            raise TypeError(repr((u, v)))
     
    -        a, b = v
    -        if isinstance(a, KleeneStar):
    -            # Two universes, in one the Kleene star disappears and unification
    -            # continues without it...
    -            s0 = unify(u, b)
    +        a, b = v
    +        if isinstance(a, KleeneStar):
    +            # Two universes, in one the Kleene star disappears and unification
    +            # continues without it...
    +            s0 = unify(u, b)
     
    -            # In the other it spawns a new variable.
    -            s1 = unify(u, (a.another(), v))
    +            # In the other it spawns a new variable.
    +            s1 = unify(u, (a.another(), v))
     
    -            t = s0 + s1
    -            for sn in t:
    -                sn.update(s)
    -            return t
    +            t = s0 + s1
    +            for sn in t:
    +                sn.update(s)
    +            return t
     
    -        a, b = u
    -        if isinstance(a, KleeneStar):
    -            s0 = unify(v, b)
    -            s1 = unify(v, (a.another(), u))
    -            t = s0 + s1
    -            for sn in t:
    -                sn.update(s)
    -            return t
    +        a, b = u
    +        if isinstance(a, KleeneStar):
    +            s0 = unify(v, b)
    +            s1 = unify(v, (a.another(), u))
    +            t = s0 + s1
    +            for sn in t:
    +                sn.update(s)
    +            return t
     
    -        ses = unify(u[0], v[0], s)
    -        results = ()
    -        for sn in ses:
    -            results += unify(u[1], v[1], sn)
    -        return results
    +        ses = unify(u[0], v[0], s)
    +        results = ()
    +        for sn in ses:
    +            results += unify(u[1], v[1], sn)
    +        return results
     
    -    if isinstance(v, tuple):
    -        if not stacky(u):
    -            raise TypeError('Cannot unify %r and %r.' % (u, v))
    -        s[u] = v
    -        return s,
    +    if isinstance(v, tuple):
    +        if not stacky(u):
    +            raise TypeError('Cannot unify %r and %r.' % (u, v))
    +        s[u] = v
    +        return s,
     
    -    if isinstance(u, tuple):
    -        if not stacky(v):
    -            raise TypeError('Cannot unify %r and %r.' % (v, u))
    -        s[v] = u
    -        return s,
    +    if isinstance(u, tuple):
    +        if not stacky(v):
    +            raise TypeError('Cannot unify %r and %r.' % (v, u))
    +        s[v] = u
    +        return s,
     
    -    return ()
    +    return ()
     
     
    -def stacky(thing):
    -    return thing.__class__ in {AnyJoyType, StackJoyType}
    +def stacky(thing):
    +    return thing.__class__ in {AnyJoyType, StackJoyType}
     
    -
    a = (As[1], S[1])
    -a
    +
    a = (As[1], S[1])
    +a
     
    (a1*, s1)
     
    -
    b = (A[1], S[2])
    -b
    +
    b = (A[1], S[2])
    +b
     
    (a1, s2)
     
    -
    for result in unify(b, a):
    -    print result, '->', update(result, a), update(result, b)
    +
    for result in unify(b, a):
    +    print result, '->', update(result, a), update(result, b)
     
    {s1: (a1, s2)} -> (a1*, (a1, s2)) (a1, s2)
     {a1: a10001, s2: (a1*, s1)} -> (a1*, s1) (a10001, (a1*, s1))
     
    -
    for result in unify(a, b):
    -    print result, '->', update(result, a), update(result, b)
    +
    for result in unify(a, b):
    +    print result, '->', update(result, a), update(result, b)
     
    {s1: (a1, s2)} -> (a1*, (a1, s2)) (a1, s2)
    @@ -1947,24 +1948,24 @@ disappears:

    (a1*, s1) [a1*] (a2, (a1*, s1)) [a2 a1*]
    -
    sum_ = ((Ns[1], S[1]), S[0]), (N[0], S[0])
    +
    sum_ = ((Ns[1], S[1]), S[0]), (N[0], S[0])
     
    -print doc_from_stack_effect(*sum_)
    +print doc_from_stack_effect(*sum_)
     
    ([n1* .1.] -- n0)
     
    -
    f = (N[1], (N[2], (N[3], S[1]))), S[0]
    +
    f = (N[1], (N[2], (N[3], S[1]))), S[0]
     
    -print doc_from_stack_effect(S[0], f)
    +print doc_from_stack_effect(S[0], f)
     
    (-- [n1 n2 n3 .1.])
     
    -
    for result in unify(sum_[0], f):
    -    print result, '->', update(result, sum_[1])
    +
    for result in unify(sum_[0], f):
    +    print result, '->', update(result, sum_[1])
     
    {s1: (n1, (n2, (n3, s1)))} -> (n0, s0)
    @@ -1973,102 +1974,102 @@ disappears:

    {n1: n10001, s1: (n1*, s1), n3: n10003, n2: n10002} -> (n0, s0)
    -
    -
    +
    +

    compose() version 3

    This function has to be modified to yield multiple results.

    -
    def compose(f, g):
    -    (f_in, f_out), (g_in, g_out) = f, g
    -    s = unify(g_in, f_out)
    -    if not s:
    -        raise TypeError('Cannot unify %r and %r.' % (f_out, g_in))
    -    for result in s:
    -        yield update(result, (f_in, g_out))
    +
    def compose(f, g):
    +    (f_in, f_out), (g_in, g_out) = f, g
    +    s = unify(g_in, f_out)
    +    if not s:
    +        raise TypeError('Cannot unify %r and %r.' % (f_out, g_in))
    +    for result in s:
    +        yield update(result, (f_in, g_out))
     
    -
    def meta_compose(F, G):
    -    for f, g in product(F, G):
    -        try:
    -            for result in C(f, g):
    -                yield result
    -        except TypeError:
    -            pass
    +
    def meta_compose(F, G):
    +    for f, g in product(F, G):
    +        try:
    +            for result in C(f, g):
    +                yield result
    +        except TypeError:
    +            pass
     
     
    -def C(f, g):
    -    f, g = relabel(f, g)
    -    for fg in compose(f, g):
    -        yield delabel(fg)
    +def C(f, g):
    +    f, g = relabel(f, g)
    +    for fg in compose(f, g):
    +        yield delabel(fg)
     
    -
    for f in MC([dup], muls):
    -    print doc_from_stack_effect(*f)
    +
    for f in MC([dup], muls):
    +    print doc_from_stack_effect(*f)
     
    (f1 -- f2)
     (i1 -- i2)
     
    -
    for f in MC([dup], [sum_]):
    -    print doc_from_stack_effect(*f)
    +
    for f in MC([dup], [sum_]):
    +    print doc_from_stack_effect(*f)
     
    ([n1* .1.] -- [n1* .1.] n1)
     
    -
    for f in MC([cons], [sum_]):
    -    print doc_from_stack_effect(*f)
    +
    for f in MC([cons], [sum_]):
    +    print doc_from_stack_effect(*f)
     
    (a1 [.1.] -- n1)
     (n1 [n1* .1.] -- n2)
     
    -
    sum_ = (((N[1], (Ns[1], S[1])), S[0]), (N[0], S[0]))
    -print doc_from_stack_effect(*cons),
    -print doc_from_stack_effect(*sum_),
    +
    sum_ = (((N[1], (Ns[1], S[1])), S[0]), (N[0], S[0]))
    +print doc_from_stack_effect(*cons),
    +print doc_from_stack_effect(*sum_),
     
    -for f in MC([cons], [sum_]):
    -    print doc_from_stack_effect(*f)
    +for f in MC([cons], [sum_]):
    +    print doc_from_stack_effect(*f)
     
    (a1 [.1.] -- [a1 .1.]) ([n1 n1* .1.] -- n0) (n1 [n1* .1.] -- n2)
     
    -
    a = (A[4], (As[1], (A[3], S[1])))
    -a
    +
    a = (A[4], (As[1], (A[3], S[1])))
    +a
     
    (a4, (a1*, (a3, s1)))
     
    -
    b = (A[1], (A[2], S[2]))
    -b
    +
    b = (A[1], (A[2], S[2]))
    +b
     
    (a1, (a2, s2))
     
    -
    for result in unify(b, a):
    -    print result
    +
    for result in unify(b, a):
    +    print result
     
    {a1: a4, s2: s1, a2: a3}
     {a1: a4, s2: (a1*, (a3, s1)), a2: a10003}
     
    -
    for result in unify(a, b):
    -    print result
    +
    for result in unify(a, b):
    +    print result
     
    {s2: s1, a2: a3, a4: a1}
     {s2: (a1*, (a3, s1)), a2: a10004, a4: a1}
     
    -
    -
    -
    -
    +
    +
    + +

    Part VII: Typing Combinators

    In order to compute the stack effect of combinators you kinda have to have the quoted programs they expect available. In the most general @@ -2095,7 +2096,7 @@ effect other than it expects one quote:

    Without any information about the contents of the quote we can’t say much about the result.

    -
    +

    Hybrid Inferencer/Interpreter

    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, @@ -2105,80 +2106,80 @@ 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.

    -
    +

    Joy Types for Functions

    We need a type variable for Joy functions that can go in our expressions and be used by the hybrid inferencer/interpreter. They have to store a name and a list of stack effects.

    -
    class FunctionJoyType(AnyJoyType):
    +
    class FunctionJoyType(AnyJoyType):
     
    -    def __init__(self, name, sec, number):
    -        self.name = name
    -        self.stack_effects = sec
    -        self.number = number
    +    def __init__(self, name, sec, number):
    +        self.name = name
    +        self.stack_effects = sec
    +        self.number = number
     
    -    def __add__(self, other):
    -        return self
    -    __radd__ = __add__
    +    def __add__(self, other):
    +        return self
    +    __radd__ = __add__
     
    -    def __repr__(self):
    -        return self.name
    +    def __repr__(self):
    +        return self.name
     
    -
    -
    +
    +

    Specialized for Simple Functions and Combinators

    For non-combinator functions the stack effects list contains stack effect comments (represented by pairs of cons-lists as described above.)

    -
    class SymbolJoyType(FunctionJoyType):
    -    prefix = 'F'
    +
    class SymbolJoyType(FunctionJoyType):
    +    prefix = 'F'
     

    For combinators the list contains Python functions.

    -
    class CombinatorJoyType(FunctionJoyType):
    +
    class CombinatorJoyType(FunctionJoyType):
     
    -    prefix = 'C'
    +    prefix = 'C'
     
    -    def __init__(self, name, sec, number, expect=None):
    -        super(CombinatorJoyType, self).__init__(name, sec, number)
    -        self.expect = expect
    +    def __init__(self, name, sec, number, expect=None):
    +        super(CombinatorJoyType, self).__init__(name, sec, number)
    +        self.expect = expect
     
    -    def enter_guard(self, f):
    -        if self.expect is None:
    -            return f
    -        g = self.expect, self.expect
    -        new_f = list(compose(f, g, ()))
    -        assert len(new_f) == 1, repr(new_f)
    -        return new_f[0][1]
    +    def enter_guard(self, f):
    +        if self.expect is None:
    +            return f
    +        g = self.expect, self.expect
    +        new_f = list(compose(f, g, ()))
    +        assert len(new_f) == 1, repr(new_f)
    +        return new_f[0][1]
     

    For simple combinators that have only one effect (like dip) you only need one function and it can be the combinator itself.

    -
    import joy.library
    +
    import joy.library
     
    -dip = CombinatorJoyType('dip', [joy.library.dip], 23)
    +dip = CombinatorJoyType('dip', [joy.library.dip], 23)
     

    For combinators that can have more than one effect (like branch) you have to write functions that each implement the action of one of the effects.

    -
    def branch_true(stack, expression, dictionary):
    -    (then, (else_, (flag, stack))) = stack
    -    return stack, concat(then, expression), dictionary
    +
    def branch_true(stack, expression, dictionary):
    +    (then, (else_, (flag, stack))) = stack
    +    return stack, concat(then, expression), dictionary
     
    -def branch_false(stack, expression, dictionary):
    -    (then, (else_, (flag, stack))) = stack
    -    return stack, concat(else_, expression), dictionary
    +def branch_false(stack, expression, dictionary):
    +    (then, (else_, (flag, stack))) = stack
    +    return stack, concat(else_, expression), dictionary
     
    -branch = CombinatorJoyType('branch', [branch_true, branch_false], 100)
    +branch = CombinatorJoyType('branch', [branch_true, branch_false], 100)
     

    You can also provide an optional stack effect, input-side only, that will then be used as an identity function (that accepts and returns stacks that match the “guard” stack effect) which will be used to guard against type mismatches going into the evaluation of the combinator.

    -
    -
    +
    +

    infer()

    With those in place, we can define a function that accepts a sequence of Joy type variables, including ones representing functions (not just @@ -2188,58 +2189,58 @@ that expression.

    updated along with the stack effects after doing unification or we risk losing useful information. This was a straightforward, if awkward, modification to the call structure of meta_compose() et. al.

    -
    ID = S[0], S[0]  # Identity function.
    +
    ID = S[0], S[0]  # Identity function.
     
     
    -def infer(*expression):
    -    return sorted(set(_infer(list_to_stack(expression))))
    +def infer(*expression):
    +    return sorted(set(_infer(list_to_stack(expression))))
     
     
    -def _infer(e, F=ID):
    -    _log_it(e, F)
    -    if not e:
    -        return [F]
    +def _infer(e, F=ID):
    +    _log_it(e, F)
    +    if not e:
    +        return [F]
     
    -    n, e = e
    +    n, e = e
     
    -    if isinstance(n, SymbolJoyType):
    -        eFG = meta_compose([F], n.stack_effects, e)
    -        res = flatten(_infer(e, Fn) for e, Fn in eFG)
    +    if isinstance(n, SymbolJoyType):
    +        eFG = meta_compose([F], n.stack_effects, e)
    +        res = flatten(_infer(e, Fn) for e, Fn in eFG)
     
    -    elif isinstance(n, CombinatorJoyType):
    -        fi, fo = n.enter_guard(F)
    -        res = flatten(_interpret(f, fi, fo, e) for f in n.stack_effects)
    +    elif isinstance(n, CombinatorJoyType):
    +        fi, fo = n.enter_guard(F)
    +        res = flatten(_interpret(f, fi, fo, e) for f in n.stack_effects)
     
    -    elif isinstance(n, Symbol):
    -        assert n not in FUNCTIONS, repr(n)
    -        func = joy.library._dictionary[n]
    -        res = _interpret(func, F[0], F[1], e)
    +    elif isinstance(n, Symbol):
    +        assert n not in FUNCTIONS, repr(n)
    +        func = joy.library._dictionary[n]
    +        res = _interpret(func, F[0], F[1], e)
     
    -    else:
    -        fi, fo = F
    -        res = _infer(e, (fi, (n, fo)))
    +    else:
    +        fi, fo = F
    +        res = _infer(e, (fi, (n, fo)))
     
    -    return res
    +    return res
     
     
    -def _interpret(f, fi, fo, e):
    -    new_fo, ee, _ = f(fo, e, {})
    -    ee = update(FUNCTIONS, ee)  # Fix Symbols.
    -    new_F = fi, new_fo
    -    return _infer(ee, new_F)
    +def _interpret(f, fi, fo, e):
    +    new_fo, ee, _ = f(fo, e, {})
    +    ee = update(FUNCTIONS, ee)  # Fix Symbols.
    +    new_F = fi, new_fo
    +    return _infer(ee, new_F)
     
     
    -def _log_it(e, F):
    -    _log.info(
    -        u'%3i %s%s',
    -        len(inspect_stack()),
    -        doc_from_stack_effect(*F),
    -        expression_to_string(e),
    -        )
    +def _log_it(e, F):
    +    _log.info(
    +        u'%3i %s ∘ %s',
    +        len(inspect_stack()),
    +        doc_from_stack_effect(*F),
    +        expression_to_string(e),
    +        )
     
    -
    -
    +
    +

    Work in Progress

    And that brings us to current Work-In-Progress. The mixed-mode inferencer/interpreter infer() function seems to work well. There @@ -2248,18 +2249,18 @@ module (FIXME link to its docs here!) should be explained… There is cruft to convert the definitions in DEFS to the new SymbolJoyType objects, and some combinators. Here is an example of output from the current code :

    -
    1/0  # (Don't try to run this cell!  It's not going to work.  This is "read only" code heh..)
    +
    1/0  # (Don't try to run this cell!  It's not going to work.  This is "read only" code heh..)
     
    -logging.basicConfig(format='%(message)s', stream=sys.stdout, level=logging.INFO)
    +logging.basicConfig(format='%(message)s', stream=sys.stdout, level=logging.INFO)
     
    -globals().update(FUNCTIONS)
    +globals().update(FUNCTIONS)
     
    -h = infer((pred, s2), (mul, s3), (div, s4), (nullary, (bool, s5)), dipd, branch)
    +h = infer((pred, s2), (mul, s3), (div, s4), (nullary, (bool, s5)), dipd, branch)
     
    -print '-' * 40
    +print '-' * 40
     
    -for fi, fo in h:
    -    print doc_from_stack_effect(fi, fo)
    +for fi, fo in h:
    +    print doc_from_stack_effect(fi, fo)
     
    ---------------------------------------------------------------------------
    @@ -2322,39 +2323,41 @@ implementation in action.

    (i2 i1 -- i3)
    -
    -
    -
    -
    +
    + + +

    Conclusion

    We built a simple type inferencer, and a kind of crude “compiler” for a subset of Joy functions. Then we built a more powerful inferencer that actually does some evaluation and explores branching code paths

    Work remains to be done:

      -
    • the rest of the library has to be covered
    • -
    • figure out how to deal with loop and genrec, etc..
    • -
    • extend the types to check values (see the appendix)
    • -
    • other kinds of “higher order” type variables, OR, AND, etc..
    • -
    • maybe rewrite in Prolog for great good?
    • -
    • definitions
        -
      • don’t permit composition of functions that don’t compose
      • -
      • auto-compile compilable functions
      • +
      • the rest of the library has to be covered

      • +
      • figure out how to deal with loop and genrec, etc..

      • +
      • extend the types to check values (see the appendix)

      • +
      • other kinds of “higher order” type variables, OR, AND, etc..

      • +
      • maybe rewrite in Prolog for great good?

      • +
      • definitions

        +
          +
        • don’t permit composition of functions that don’t compose

        • +
        • auto-compile compilable functions

      • -
      • Compiling more than just the Yin functions.
      • -
      • getting better visibility (than Python debugger.)
      • -
      • DOOOOCS!!!! Lots of docs!
          -
        • docstrings all around
        • -
        • improve this notebook (it kinda falls apart at the end +
        • Compiling more than just the Yin functions.

        • +
        • getting better visibility (than Python debugger.)

        • +
        • DOOOOCS!!!! Lots of docs!

          +
            +
          • docstrings all around

          • +
          • improve this notebook (it kinda falls apart at the end narratively. I went off and just started writing code to see if it would work. It does, but now I have to come back and describe here -what I did.

          • +what I did.

        -
    -
    + +

    Appendix: Joy in the Logical Paradigm

    For type checking to work the type label classes have to be modified to let T >= t succeed, where e.g. T is IntJoyType and t @@ -2363,115 +2366,65 @@ relational nature of the stack effect comments to “compute in reverse” as it were. There’s a working demo of this at the end of the types module. But if you’re interested in all that you should just use Prolog!

    Anyhow, type checking is a few easy steps away.

    -
    def _ge(self, other):
    -    return (issubclass(other.__class__, self.__class__)
    -            or hasattr(self, 'accept')
    -            and isinstance(other, self.accept))
    +
    def _ge(self, other):
    +    return (issubclass(other.__class__, self.__class__)
    +            or hasattr(self, 'accept')
    +            and isinstance(other, self.accept))
     
    -AnyJoyType.__ge__ = _ge
    -AnyJoyType.accept = tuple, int, float, long, str, unicode, bool, Symbol
    -StackJoyType.accept = tuple
    +AnyJoyType.__ge__ = _ge
    +AnyJoyType.accept = tuple, int, float, long, str, unicode, bool, Symbol
    +StackJoyType.accept = tuple
     
    -
    -
    +
    +
    +
    @@ -2512,7 +2464,7 @@ module. But if you’re interested in all that you should just use Prolog!


    Thun Documentation by Simon Forman is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
    Based on a work at https://osdn.net/projects/joypy/. - Created using Sphinx 1.7.3. + Created using Sphinx 4.3.0. diff --git a/docs/sphinx_docs/_build/html/notebooks/Zipper.html b/docs/sphinx_docs/_build/html/notebooks/Zipper.html index d38e5b8..ff13a07 100644 --- a/docs/sphinx_docs/_build/html/notebooks/Zipper.html +++ b/docs/sphinx_docs/_build/html/notebooks/Zipper.html @@ -1,19 +1,18 @@ - + - + - - + + + Traversing Datastructures with Zippers — Thun 0.4.1 documentation - - - - - - - + + + + + + @@ -30,9 +29,11 @@
    + +
    -
    +

    Traversing Datastructures with Zippers

    This notebook is about using the “zipper” with joy datastructures. See the Zipper wikipedia @@ -41,24 +42,24 @@ the original paper:

    from notebook_preamble import J, V, define
    +
    from notebook_preamble import J, V, define
     
    -
    +

    Trees

    In Joypy there aren’t any complex datastructures, just ints, floats, strings, Symbols (strings that are names of functions) and sequences (aka lists, aka quoted literals, aka aggregates, etc…), but we can build trees out of sequences.

    -
    J('[1 [2 [3 4 25 6] 7] 8]')
    +
    J('[1 [2 [3 4 25 6] 7] 8]')
     
    [1 [2 [3 4 25 6] 7] 8]
     
    -
    -
    +
    +

    Zipper in Joy

    Zippers work by keeping track of the current item, the already-seen items, and the yet-to-be seen items as you traverse a datastructure (the @@ -74,13 +75,13 @@ datastructure used to keep track of these items is the zipper.)

    show the trace so you can see how it works. If we were going to use these a lot it would make sense to write Python versions for efficiency, but see below.

    -
    define('z-down == [] swap uncons swap')
    -define('z-up == swons swap shunt')
    -define('z-right == [swons] cons dip uncons swap')
    -define('z-left == swons [uncons swap] dip swap')
    +
    define('z-down == [] swap uncons swap')
    +define('z-up == swons swap shunt')
    +define('z-right == [swons] cons dip uncons swap')
    +define('z-left == swons [uncons swap] dip swap')
     
    -
    V('[1 [2 [3 4 25 6] 7] 8] z-down')
    +
    V('[1 [2 [3 4 25 6] 7] 8] z-down')
     
                              . [1 [2 [3 4 25 6] 7] 8] z-down
    @@ -92,7 +93,7 @@ but see below.

    [] [[2 [3 4 25 6] 7] 8] 1 .
    -
    V('[] [[2 [3 4 25 6] 7] 8] 1 z-right')
    +
    V('[] [[2 [3 4 25 6] 7] 8] 1 z-right')
     
                                      . [] [[2 [3 4 25 6] 7] 8] 1 z-right
    @@ -112,43 +113,43 @@ but see below.

    [1] [8] [2 [3 4 25 6] 7] .
    -
    J('[1] [8] [2 [3 4 25 6] 7] z-down')
    +
    J('[1] [8] [2 [3 4 25 6] 7] z-down')
     
    [1] [8] [] [[3 4 25 6] 7] 2
     
    -
    J('[1] [8] [] [[3 4 25 6] 7] 2 z-right')
    +
    J('[1] [8] [] [[3 4 25 6] 7] 2 z-right')
     
    [1] [8] [2] [7] [3 4 25 6]
     
    -
    J('[1] [8] [2] [7] [3 4 25 6] z-down')
    +
    J('[1] [8] [2] [7] [3 4 25 6] z-down')
     
    [1] [8] [2] [7] [] [4 25 6] 3
     
    -
    J('[1] [8] [2] [7] [] [4 25 6] 3 z-right')
    +
    J('[1] [8] [2] [7] [] [4 25 6] 3 z-right')
     
    [1] [8] [2] [7] [3] [25 6] 4
     
    -
    J('[1] [8] [2] [7] [3] [25 6] 4 z-right')
    +
    J('[1] [8] [2] [7] [3] [25 6] 4 z-right')
     
    [1] [8] [2] [7] [4 3] [6] 25
     
    -
    J('[1] [8] [2] [7] [4 3] [6] 25 sqr')
    +
    J('[1] [8] [2] [7] [4 3] [6] 25 sqr')
     
    [1] [8] [2] [7] [4 3] [6] 625
     
    -
    V('[1] [8] [2] [7] [4 3] [6] 625 z-up')
    +
    V('[1] [8] [2] [7] [4 3] [6] 625 z-up')
     
                                  . [1] [8] [2] [7] [4 3] [6] 625 z-up
    @@ -167,24 +168,24 @@ but see below.

    [1] [8] [2] [7] [3 4 625 6] .
    -
    J('[1] [8] [2] [7] [3 4 625 6] z-up')
    +
    J('[1] [8] [2] [7] [3 4 625 6] z-up')
     
    [1] [8] [2 [3 4 625 6] 7]
     
    -
    J('[1] [8] [2 [3 4 625 6] 7] z-up')
    +
    J('[1] [8] [2 [3 4 625 6] 7] z-up')
     
    [1 [2 [3 4 625 6] 7] 8]
     
    -
    -
    +
    +

    dip and infra

    In Joy we have the dip and infra combinators which can “target” or “address” any particular item in a Joy tree structure.

    -
    V('[1 [2 [3 4 25 6] 7] 8] [[[[[[sqr] dipd] infra] dip] infra] dip] infra')
    +
    V('[1 [2 [3 4 25 6] 7] 8] [[[[[[sqr] dipd] infra] dip] infra] dip] infra')
     
                                                                    . [1 [2 [3 4 25 6] 7] 8] [[[[[[sqr] dipd] infra] dip] infra] dip] infra
    @@ -222,8 +223,8 @@ the subject datastructure. Instead of maintaining temporary results on
     the stack they are pushed into the pending expression (continuation).
     When sqr has run the rest of the pending expression rebuilds the
     datastructure.

    -
    -
    +
    +

    Z

    Imagine a function Z that accepts a sequence of dip and infra combinators, a quoted program [Q], and a datastructure to @@ -235,11 +236,11 @@ been embedded in a nested series of quoted programs, e.g.:

    The Z function isn’t hard to make.

    -
    define('Z == [[] cons cons] step i')
    +
    define('Z == [[] cons cons] step i')
     

    Here it is in action in a simplified scenario.

    -
    V('1 [2 3 4] Z')
    +
    V('1 [2 3 4] Z')
     
                                 . 1 [2 3 4] Z
    @@ -272,14 +273,14 @@ been embedded in a nested series of quoted programs, e.g.:

    And here it is doing the main thing.

    -
    J('[1 [2 [3 4 25 6] 7] 8] [sqr] [dip dip infra dip infra dip infra] Z')
    +
    J('[1 [2 [3 4 25 6] 7] 8] [sqr] [dip dip infra dip infra dip infra] Z')
     
    [1 [2 [3 4 625 6] 7] 8]
     
    -
    -
    + +

    Addressing

    Because we are only using two combinators we could replace the list with a string made from only two characters.

    @@ -290,8 +291,8 @@ a string made from only two characters.

    The string can be considered a name or address for an item in the subject datastructure.

    -
    -
    + +

    Determining the right “path” for an item in a tree.

    It’s easy to read off (in reverse) the right sequence of “d” and “i” from the subject datastructure:

    @@ -299,27 +300,55 @@ from the subject datastructure:

    i d i d i d d Bingo!
    -
    -
    + +
    +
    @@ -360,7 +388,7 @@ i d i d i d d Bingo!
    Thun Documentation by Simon Forman is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
    Based on a work at https://osdn.net/projects/joypy/. - Created using Sphinx 1.7.3. + Created using Sphinx 4.3.0. diff --git a/docs/sphinx_docs/_build/html/notebooks/index.html b/docs/sphinx_docs/_build/html/notebooks/index.html index c8887e7..6ab069e 100644 --- a/docs/sphinx_docs/_build/html/notebooks/index.html +++ b/docs/sphinx_docs/_build/html/notebooks/index.html @@ -1,19 +1,18 @@ - + - + - - + + + Essays about Programming in Joy — Thun 0.4.1 documentation - - - - - - - + + + + + + @@ -30,9 +29,11 @@
    + +
    -
    +

    Essays about Programming in Joy

    These essays are adapted from Jupyter notebooks. I hope to have those hosted somewhere where people can view them “live” and interact with them, possibly on MS Azure. For now, Sphinx does such a great job rendering the HTML that I am copying over some notebooks in ReST format and hand-editing them into these documents.

    @@ -156,14 +157,55 @@
    -
    +
    +
    @@ -143,7 +161,7 @@ Any unbalanced square brackets will raise a ParseError.


    Thun Documentation by Simon Forman is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
    Based on a work at https://osdn.net/projects/joypy/. - Created using Sphinx 1.7.3. + Created using Sphinx 4.3.0.
    diff --git a/docs/sphinx_docs/_build/html/pretty.html b/docs/sphinx_docs/_build/html/pretty.html index f393797..9ac1af8 100644 --- a/docs/sphinx_docs/_build/html/pretty.html +++ b/docs/sphinx_docs/_build/html/pretty.html @@ -1,19 +1,18 @@ - + - + - - + + + Tracing Joy Execution — Thun 0.4.1 documentation - - - - - - - + + + + + + @@ -30,11 +29,13 @@
    + +
    -
    +

    Tracing Joy Execution

    -
    +

    joy.utils.pretty_print

    Pretty printing support, e.g.:

    Joy? [23 18 * 99 +] trace
    @@ -53,89 +54,97 @@ joy?
     

    On each line the stack is printed with the top to the left, then a bullet symbol,``•``, to represent the current locus of processing, then the pending expression to the right.

    -
    -
    -class joy.utils.pretty_print.TracePrinter[source]
    +
    +
    +class joy.utils.pretty_print.TracePrinter[source]

    This is what does the formatting. You instantiate it and pass the viewer() method to the joy.joy.joy() function, then print it to see the trace.

    -
    -
    -go()[source]
    +
    +
    +go()[source]

    Return a list of strings, one for each entry in the history, prefixed with enough spaces to align all the interpreter dots.

    This method is called internally by the __str__() method.

    - --- - - - -
    Return type:list(str)
    +
    +
    Return type
    +

    list(str)

    +
    +
    -
    -
    -viewer(stack, expression)[source]
    +
    +
    +viewer(stack, expression)[source]

    Record the current stack and expression in the TracePrinter’s history. Pass this method as the viewer argument to the joy.joy.joy() function.

    - --- - - - -
    Parameters:
      -
    • quote (stack) – A stack.
    • -
    • expression (stack) – A stack.
    • +
      +
      Parameters
      +
        +
      • quote (stack) – A stack.

      • +
      • expression (stack) – A stack.

      -
    +
    +
    -
    -
    -joy.utils.pretty_print.trace(stack, expression, dictionary)[source]
    +
    +
    +joy.utils.pretty_print.trace(stack, expression, dictionary)[source]

    Evaluate a Joy expression on a stack and print a trace.

    This function is just like the i combinator but it also prints a trace of the evaluation

    - --- - - - - - -
    Parameters:
      -
    • stack (stack) – The stack.
    • -
    • expression (stack) – The expression to evaluate.
    • -
    • dictionary (dict) – A dict mapping names to Joy functions.
    • +
      +
      Parameters
      +
        +
      • stack (stack) – The stack.

      • +
      • expression (stack) – The expression to evaluate.

      • +
      • dictionary (dict) – A dict mapping names to Joy functions.

      -
    Return type:

    (stack, (), dictionary)

    -
    +
    +
    Return type
    +

    (stack, (), dictionary)

    +
    +
    -
    -
    +
    +
    +
    @@ -174,7 +182,7 @@ trace of the evaluation


    Thun Documentation by Simon Forman is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
    Based on a work at https://osdn.net/projects/joypy/. - Created using Sphinx 1.7.3. + Created using Sphinx 4.3.0.
    diff --git a/docs/sphinx_docs/_build/html/py-modindex.html b/docs/sphinx_docs/_build/html/py-modindex.html index 569ddf3..7c96010 100644 --- a/docs/sphinx_docs/_build/html/py-modindex.html +++ b/docs/sphinx_docs/_build/html/py-modindex.html @@ -1,19 +1,17 @@ - + - + - - + + Python Module Index — Thun 0.4.1 documentation - - - - - - - + + + + + + @@ -31,6 +29,8 @@
    + +
    @@ -84,10 +84,34 @@
    +