Skip to content

Commit

Permalink
make TCO implementation switchable at run time; default to the except…
Browse files Browse the repository at this point in the history
…ion-based TCO (slower, but easier syntax)
  • Loading branch information
Technologicat committed Aug 10, 2018
1 parent a63948a commit fec74e3
Show file tree
Hide file tree
Showing 8 changed files with 576 additions and 474 deletions.
95 changes: 72 additions & 23 deletions README.md

Large diffs are not rendered by default.

14 changes: 10 additions & 4 deletions quick_tour.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,18 @@ def x():

# tail call optimization (TCO) (w.r.t. stack space, not speed!)
@trampolined
def fact(n, acc=1):
def even(n):
if n == 0:
return acc
return True
else:
return jump(odd, n - 1)
@trampolined
def odd(n):
if n == 0:
return False
else:
return jump(fact, n - 1, n * acc)
fact(10000) # no crash
return jump(even, n - 1)
assert even(10000) is True # no crash

# FP loop
@looped
Expand Down
9 changes: 8 additions & 1 deletion tour.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from unpythonic import assignonce, \
dyn, \
let, letrec, dlet, dletrec, blet, bletrec, \
call, begin, begin0, lazy_begin, lazy_begin0, \
call, begin, begin0, lazy_begin, lazy_begin0, do, \
trampolined, jump, looped, looped_over, SELF, \
setescape, escape, call_ec

Expand Down Expand Up @@ -141,6 +141,13 @@ def result():
lambda: print("cheeky side effect"))
assert f4(2) == 84

# sequencing operations starting from an initial value
#
double = lambda x: 2 * x
inc = lambda x: x + 1
assert do(42, double, inc) == 85
assert do(42, inc, double) == 86

# tail recursion with tail call optimization (TCO)
@trampolined
def fact(n, acc=1):
Expand Down
66 changes: 48 additions & 18 deletions unpythonic/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,36 +2,66 @@
# -*- coding: utf-8 -*
"""Lispy missing batteries for Python.
We provide two submodules which implement the ``let`` construct:
- ``unpythonic.let``:
Pythonic syntax, but no guarantees on evaluation order of the bindings
(until Python 3.6; see https://www.python.org/dev/peps/pep-0468/ ).
Bindings are declared using kwargs.
- ``unpythonic.lispylet``:
Guaranteed left-to-right evaluation of bindings, but clunky syntax.
Bindings are declared as ``(("name", value), ...)``.
With ``import unpythonic``, the default ``let`` construct is ``unpythonic.let``.
To override, just import the other one; they define the same names.
See ``dir(unpythonic)`` and submodule docstrings for more.
"""

__version__ = '0.5.0'

from . import rc

from .arity import *
from .assignonce import *
from .dynscope import *
from .ec import *
from .fploop import *
from .let import * # no guarantees on evaluation order (before Python 3.6), nice syntax
from .let import * # no guarantees on evaluation order (before Python 3.6), nice syntax

# guaranteed evaluation order, clunky syntax
from .lispylet import let as ordered_let, letrec as ordered_letrec, \
dlet as ordered_dlet, dletrec as ordered_dletrec, \
blet as ordered_blet, bletrec as ordered_bletrec

from .misc import *
from .tco import *

__version__ = '0.5.0'
# Jump through hoops to get a runtime-switchable TCO implementation.
#
# We manually emulate:
# - making the submodule visible like __init__.py usually does
# - from submod import *
#
def _init_tco():
global tco
if rc._tco_impl == "exc":
from . import tco
elif rc._tco_impl == "fast":
from . import fasttco as tco
else:
raise ValueError("Unknown TCO implementation '{}'".format(rc._tco_impl))
g = globals()
for name in tco.__all__:
g[name] = getattr(tco, name)

def _init_fploop(reload=False):
global fploop
from . import fploop
# We must reload fploop, because its module-level initialization
# performs some imports from the TCO module.
if reload:
from importlib import reload
fploop = reload(fploop)
g = globals()
for name in fploop.__all__:
g[name] = getattr(fploop, name)

_init_tco()
_init_fploop()

def enable_fasttco():
"""Switch to the fast TCO implementation.
It is 2-5x faster, but pickier about its syntax, hence not the default.
See ``unpythonic.fasttco`` for details.
"""
rc._tco_impl = "fast"

_init_tco()
_init_fploop(reload=True)
Loading

0 comments on commit fec74e3

Please sign in to comment.