0.15.0 (in progress; updated 19 May 2021) - "We say 'howdy' around these parts" edition:
Beside introducing dialects (a.k.a. whole-module code transforms), this edition concentrates on upgrading our dependencies, namely the macro expander, and the Python language itself, to ensure unpythonic keeps working for the next few years. This introduces some breaking changes, so we have also taken the opportunity to apply any such that were previously scheduled.
We have sneaked in some upgrades for other subsystems, too. Particularly curry, the multiple dispatch system (@generic), and the integration between these two have been improved significantly.
IMPORTANT:
- Minimum Python language version is now 3.6.
- We support 3.6, 3.7, 3.8, 3.9 and PyPy3 (language versions 3.6 and 3.7).
- For future plans, see our Python language version support status.
- The optional macro expander is now
mcpyrate.
If you still need unpythonic for Python 3.4 or 3.5, use version 0.14.3, which is the final version of unpythonic that supports those language versions.
The same applies if you need the macro parts of unpythonic (i.e. import anything from unpythonic.syntax) in your own project that uses MacroPy. Version 0.14.3 of unpythonic works up to Python 3.7.
New:
-
Dialects! New module
unpythonic.dialects, providing some example dialects that demonstrate what can be done with a dialects system (i.e. full-module code transformer) together with a kitchen-sink language extension macro package such asunpythonic. -
Improved robustness: several auxiliary syntactic constructs now detect at macro expansion time if they appear outside any valid lexical context, and raise
SyntaxError(with a descriptive message) if so.- The full list is:
call_cc[], forwith continuationsit, foraif[]local[]/delete[], fordo[]q/u/kw, forwith prefixwhere, forlet[body, where(k0=v0, ...)](also forletseq,letrec,let_syntax,abbrev)with expr/with block, forwith let_syntax/with abbrev
- Previously these constructs could only raise an error at run time, and not all of them could detect the error even then.
- The full list is:
-
Syntactic consistency: allow env-assignment notation and brackets to declare bindings in the
letfamily of macros. The preferred syntaxes for theletmacro are now:let[x << 42, y << 9001][...] # lispy expr let[[x << 42, y << 9001] in ...] # haskelly let-in let[..., where[x << 42, y << 9001]] # haskelly let-where
If there is just one binding, these become:
let[x << 42][...] let[[x << 42] in ...] let[..., where[x << 42]]
Similarly for
letseq,letrec, and the decorator versions; and for the expr forms oflet_syntax,abbrev. The reason for preferring this notation is that it is consistent with bothunpythonic's env-assignments (letbindings live in anenv) and the use of brackets to denote macro invocations.To ease backwards compatibility, we still accept the syntax used up to v0.14.3, too.
Also, from symmetry and usability viewpoints, if a mix of brackets and parentheses are used, it hardly makes sense to require some specific mix - so this has been extended so that the choice of delimiter doesn't matter. All the following are also accepted, with the meaning exactly the same as above:
let[[x, 42], [y, 9001]][...] # best visual consistency let[(x, 42), (y, 9001)][...] let([x, 42], [y, 9001])[...] let((x, 42), (y, 9001))[...] # like up to v0.14.3 let[[[x, 42], [y, 9001]] in ...] # best visual consistency let[[(x, 42), (y, 9001)] in ...] let[([x, 42], [y, 9001]) in ...] let[((x, 42), (y, 9001)) in ...] # like up to v0.14.3 let[(x << 42, y << 9001) in ...] let[..., where[[x, 42], [y, 9001]]] # best visual consistency let[..., where[(x, 42), (y, 9001)]] let[..., where([x, 42], [y, 9001])] let[..., where((x, 42), (y, 9001))] # like up to v0.14.3 let[..., where(x << 42, y << 9001)]
For a single binding, these are also accepted:
let[x, 42][...] let(x, 42)[...] # like up to v0.14.3 let[[x, 42] in ...] let[(x, 42) in ...] # like up to v0.14.3 let[(x << 42) in ...] let[..., where[x, 42]] let[..., where(x, 42)] # like up to v0.14.3 let[..., where(x << 42)]
These alternate syntaxes will be supported at least as long as we accept parentheses to pass macro arguments; but in new code, please use the preferred syntaxes.
-
Miscellaneous.
with namedlambdanow understands the walrus operator, too. In the constructf := lambda ...: ..., the lambda will get the namef. (Python 3.8 and later.)with namedlambdanow auto-names lambdas that don't have a name candidate using their source location info, if present. This makes it easy to see in a stack trace where some particular lambda was defined.- Multiple-dispatch system
unpythonic.dispatch:- Use consistent terminology:
- The function that supports multiple call signatures is a generic function.
- Its individual implementations are multimethods.
- Add decorator
@augment: add a multimethod to a generic function defined elsewhere. - Add function
isgenericto detect whether a callable has been declared@generic. - Add function
methods: display a list of multimethods of a generic function. - It is now possible to dispatch on a homogeneous type of contents collected by a
**kwargsparameter. currynow supports@genericfunctions. This feature is experimental. Semantics may still change.- The utilities
arities,required_kwargs, andoptional_kwargsnow support@genericfunctions. This feature is experimental. Semantics may still change.
- Use consistent terminology:
currynow errors out immediately on argument type mismatch.- Add
partial, a type-checking wrapper forfunctools.partial, that errors out immediately on argument type mismatch. - Add
unpythonic.excutil.reraise_in(expr form),unpythonic.excutil.reraise(block form): conveniently remap library exception types to application exception types. Idea from Alexis King (2016): Four months with Haskell. - Add variants of the above for the conditions-and-restarts system:
unpythonic.conditions.resignal_in,unpythonic.conditions.resignal. The new signal is sent using the same error-handling protocol as the original signal, so that e.g. anerrorremains anerroreven if re-signaling changes its type. - Add
resolve_bindings_partial, useful for analyzing partial application. - All documentation files now have a quick navigation section to skip to another part of the docs. (For all except the README, it's at the top.)
- Python 3.8 and 3.9 support added.
Non-breaking changes:
-
Changes to how some macros expand.
-
Some macros, notably
letseq,do0, andlazyrec, now expand into hygienic macro captures of other macros. Thecontinuationsmacro also outputs a hygienically capturedaifwhen transforming anorexpression that occurs in tail position.- This allows
mcpyrate.debug.step_expansionto show the intermediate result, as well as brings the implementation closer to the natural explanation of how these macros are defined. (Zen of Python: if the implementation is easy to explain, it might be a good idea.) - The implicit do (extra bracket syntax) also expands as a hygienically captured
do, but e.g. inlet[]it will then expand immediately (due tolet's inside-out expansion order) before control returns to the macro stepper. If you want to see the implicitdo[]invocation, use the"detailed"mode of the stepper, which shows individual macro invocations even when expanding inside-out:step_expansion["detailed"][...],with step_expansion["detailed"]:.
- This allows
-
The
do[]anddo0[]macros now expand outside-in. The main differences from a user perspective are:- Any source code captures (such as those performed by
test[]) show the expanded output ofdoanddo0, because that's what they receive. (For tests, you may want to use the macrowith expand_testing_macros_first, which see.) mcpyrate.debug.step_expansionis able to show the intermediate result after thedoordo0has expanded, but before anything else has been done to the tree.
- Any source code captures (such as those performed by
-
-
Miscellaneous.
- Resolve issue #61:
currynow supports kwargs properly.- We now analyze parameter bindings like Python itself does, so it should no longer matter whether arguments are passed by position or by name.
- Positional passthrough works as before. Named passthrough added.
- Any remaining arguments (that cannot be accepted by the initial call) are passed through to a callable intermediate result (if any), and then outward on the curry context stack as a
Values. Sincecurryin this role is essentially a function-composition utility, the receiving curried function instance unpacks theValuesinto args and kwargs. - If any extra arguments (positional or named) remain when the top-level curry context exits, then by default,
TypeErroris raised. To override, usewith dyn.let(curry_context=["whatever"]), just like before. Then you'll get aValuesobject.
unpythonic.conditions.signal, when the signal goes unhandled, now returns the canonized inputcondition, with a nice traceback attached. This feature is intended for implementing custom error protocols on top ofsignal;erroralready uses it to produce a nice-looking error report.- The modules
unpythonic.dispatchandunpythonic.typecheck, which provide the@genericand@typeddecorators and theisoftypefunction, are no longer considered experimental. From this release on, they receive the same semantic versioning guarantees as the rest ofunpythonic. - CI: Automated tests now run on Python 3.6, 3.7, 3.8, 3.9, and PyPy3 (language versions 3.6, 3.7).
- CI: Test coverage improved to 94%.
- Resolve issue #61:
Breaking changes:
-
New macro expander
mcpyrate; MacroPy support dropped.- API differences.
- Macro arguments are now passed using brackets,
macroname[args][...],with macroname[args],@macroname[args], instead of parentheses.- Parentheses are still available as alternative syntax, because up to Python 3.8, decorators cannot have subscripts (so e.g.
@dlet[(x, 42)]is a syntax error, but@dlet((x, 42))is fine). This has been fixed in Python 3.9. - If you already only run on Python 3.9 and later, please use brackets, that is the preferred syntax. We currently plan to eventually drop support for parentheses to pass macro arguments in the future, when Python 3.9 becomes the minimum supported language version for
unpythonic.
- Parentheses are still available as alternative syntax, because up to Python 3.8, decorators cannot have subscripts (so e.g.
- If you write your own macros, note
mcpyrateis not drop-in compatible with MacroPy ormcpy. See its documentation for details.
- Macro arguments are now passed using brackets,
- Behavior differences.
mcpyrateshould report test coverage for macro-using code correctly; no need for# pragma: no coverin block macro invocations or in quasiquoted code.
- API differences.
-
Previously scheduled API changes.
- As promised, names deprecated during 0.14.x have been removed. Old name on the left, new name on the right:
m→imathify(consistency with the rest ofunpythonic)mg→gmathify(consistency with the rest ofunpythonic)setescape→catch(Lisp family standard name)escape→throw(Lisp family standard name)getvalue,runpipe→exitpipe(combined into one)- CAUTION:
exitpipealready existed in v0.14.3, but beginning with v0.15.0, it is now anunpythonic.symbol.sym(like a Lisp symbol). This is not compatible with existing, pickledexitpipeinstances; it used to be an instance of the classGetvalue, which has been removed. (There's not much reason to pickle anexitpipeinstance, but we're mentioning this for the sake of completeness.)
- CAUTION:
- Drop support for deprecated argument format for
raisef. Now the usage israisef(exc)orraisef(exc, cause=...). These correspond exactly toraise excandraise exc from ..., respectively.
- As promised, names deprecated during 0.14.x have been removed. Old name on the left, new name on the right:
-
Other backward-incompatible API changes.
- Multiple-return-value handling changed. Resolves issue #32.
- Multiple return values are now denoted as
Values, available from the top-level namespace ofunpythonic. - The
Valuesconstructor accepts both positional and named arguments. Passing in named arguments creates named return values. This completes the symmetry between argument passing and returns. - Most of the time, it's still fine to return a tuple and destructure that; but in contexts where it is important to distinguish between a single
tuplereturn value and multiple return values, it is preferable to useValues. - In any utilities that deal with function composition, if your intent is multiple-return-values, it is now mandatory to return a
Valuesinstead of atuple:currypipefamilycomposefamily- All multiple-return-values in code using the
with continuationsmacro. (The continuations system essentially composes continuation functions.)
- Multiple return values are now denoted as
- The lazy evaluation tools
lazy,Lazy, and the quick lambdaf(underscore notation for Python) are now provided byunpythonicasunpythonic.syntax.lazy,unpythonic.lazyutil.Lazy, andunpythonic.syntax.f, because they used to be provided bymacropy, andmcpyratedoes not provide them.- API differences.
- The macros
lazyandfcan be imported from the syntax interface module,unpythonic.syntax, and the classLazyis available at the top level ofunpythonic. - Unlike
macropy'sLazy, ourLazydoes not define__call__; instead, it defines the methodforce, which has the same effect (it computes if necessary, and then returns the value of the promise). - When you import the macro
quicklambda, you must import also the macrof. - The underscore
_is no longer a macro on its own. Thefmacro treats the underscore magically, as before, but anywhere else it is available to be used as a regular variable.
- The macros
- Behavior differences.
f[]now respects nesting: an invocation off[]will not descend into another nestedf[].- The
with quicklambdamacro is still provided, and used just as before. Now it causes anyf[]invocations lexically inside the block to expand before any other macros in that block do. - Since in
mcpyrate, macros can be as-imported, you can renamefat import time to have any name you want. Thequicklambdablock macro respects the as-import, by internally querying the expander to determine the name(s) the macrofis currently bound to.
- API differences.
- Rename the
currymacro toautocurry, to prevent name shadowing of thecurryfunction. The new name is also more descriptive. - Move the functions
force1andforcefromunpythonic.syntaxtounpythonic. Make theLazyclass (promise implementation) public. (They actually come fromunpythonic.lazyutil.) - Change parameter ordering of
unpythonic.it.windowto make it curry-friendly. Usage is nowwindow(n, iterable).- This was an oversight when this function was added; most other functions in
unpythonic.ithave been curry-friendly from the beginning.
- This was an oversight when this function was added; most other functions in
- Change output format of
resolve_bindingsto return aninspect.BoundArgumentsinstead of the previousOrderedDictthat had a custom format. Change the input format oftuplify_bindingsto match. - Change parameter name from
ltolengthin the functionsin_sliceandindex_in_slice(in theunpythonic.collectionsmodule).- These are mostly used internally, but technically a part of the public API.
- This change fixes a
flake8E741 warning, and the new name for the parameter is more descriptive.
- Multiple-return-value handling changed. Resolves issue #32.
-
Miscellaneous.
- The functions
raisef,tryf,equip_with_traceback, andasync_raisenow live inunpythonic.excutil. They are still available in the top-level namespace ofunpythonic, as usual. - The functions
callandcallwithnow live inunpythonic.funutil. They are still available in the top-level namespace ofunpythonic, as usual. - The functions
almosteqandulpnow live inunpythonic.numutil. They are still available in the top-level namespace ofunpythonic, as usual. - Remove the internal utility class
unpythonic.syntax.util.ASTMarker. We now havemcpyrate.markers.ASTMarker, which is designed for data-driven communication between macros that work together. As a bonus, no markers are left in the AST at run time. - Rename contribution guidelines to
CONTRIBUTING.md, which is the modern standard name. Old name wasHACKING.md, which was correct, but nowadays obscure. - Python 3.4 and 3.5 support dropped, as these language versions have officially reached end-of-life.
- The functions
Fixed:
-
Make
unpythonic.misc.callsite_filenameignore our call helpers. This allows the testing framework report the source code filename correctly when testing code using macros that make use of these helpers (e.g.autocurry,lazify). -
In
aif,itis now only valid in thethenandotherwiseparts, as it should always have been. -
Fix docstring of
test: multiplethe[]marks were already supported in 0.14.3, as the macro documentation already said, but the docstring claimed otherwise. -
Fix bug in
with namedlambda. Due to incorrect function arguments in the analyzer, already named lambdas were not detected correctly.
0.14.3 (13 April 2021) - Testing and all that jazz edition:
New:
unpythonic.test.fixtures, a lightweight testing framework for macro-enabled Python code.- Context managers
session,testset, andcatch_signals. Various helper functions, such asreturns_normally(for use in atest[]). - Testing macros, similar to the builtin
assert, but with the magic of conditions and restarts: even if a test fails or errors out, further tests continue running.test[expr],test[expr, message],test_raises[exctype, expr],test_raises[exctype, expr, message],test_signals[exctype, expr],test_signals[exctype, expr, message].- To help diagnose test failures with minimum fuss, the
test[...]macro provides an optional markerthe[expr]to capture the values of interesting subexpressions inside atest[...], for display in the test failure message (along with the corresponding source code).- Often even that is not needed; by default, if no
the[]are present,test[]captures the value of the leftmost term when the test is a comparison (common use case).
- Often even that is not needed; by default, if no
- Helper macros
fail[message],error[message]andwarn[message]for producing unconditional failures, errors or warnings.
- Context managers
callsite_filename: return the filename from which this function is being called. Useful as a building block for debug utilities and similar.equip_with_traceback: take a manually created exception instance, equip it with a traceback. Requires Python 3.7 or later.subset: test whether an iterable is a subset of another. Convenience function.allsame: test whether all elements of an iterable are the same. Sometimes useful in writing testing code.safeissubclass: like issubclass, but ifclsis not a class, swallow theTypeErrorand returnFalse. Sometimes useful when dealing with lots of code that needs to check types dynamically.
Non-breaking changes:
snow has a convenience mode for generating cyclic infinite sequences.mis nowimathifyandmgis nowgmathify, for descriptiveness, and for consistency with naming other abstractions inunpythonic. The old names will remain working in v0.14.x, and will be removed in v0.15.0.@genericand@typedcan now decorate instance methods, class methods and static methods. This makes those methods (OOP sense) have methods (generic function sense). Get it?selfandclsparameters do not participate in dispatching, and need no type annotation.- Beside appearing as the first positional-or-keyword parameter, the self-like parameter must be named one of
self,this,cls, orklassto be detected by the ignore mechanism. This limitation is due to implementation reasons; while a class body is being evaluated, the context needed to distinguish a method (OOP sense) from a regular function is not yet present. - OOP inheritance support: when
@genericis installed on an OOP method (instance method, or@classmethod), then at call time, classes are tried in MRO order. All generic-function methods of the OOP method defined in the class currently being looked up are tested for matches first, before moving on to the next class in the MRO. (This has subtle consequences, related to in which class in the hierarchy the various generic-function methods for a particular OOP method are defined.) - To work with OOP inheritance,
@genericmust be the outermost decorator (except@classmethodor@staticmethod, which are essentially compiler annotations). - However, when installed on a
@staticmethod, the@genericdecorator does not support MRO lookup, because that would make no sense. See discussions on interaction between@staticmethodandsuperin Python: [1] [2].
- To ease installation, relax version requirement of the optional MacroPy dependency to the latest released on PyPI, 1.1.0b2.
- Once MacroPy updates, we'll upgrade; 1.1.0b2 is missing some small features we would like to use (particularly the
.transformattribute of macros, which allows calling the underlying syntax transformer function).
- Once MacroPy updates, we'll upgrade; 1.1.0b2 is missing some small features we would like to use (particularly the
- Conditions: when an unhandled
errororcerroroccurs, the original unhandled error is now available in the__cause__attribute of theControlErrorexception that is raised in this situation. - Conditions: on Python 3.7+,
signalnow equips the condition instance with a traceback, for consistency withraise. - Document named-arg bug in
curryin the docstring. See #61. Fixing this needs a betterpartial, so for now it's a known issue. - All of
unpythonicitself is now tested using the new testing framework for macro-enabled code,unpythonic.test.fixtures. Hence, developingunpythonicnow requires MacroPy. For usingunpythonic, MacroPy remains strictly optional, as it will at least for the foreseeable future.
Breaking changes:
- Experimental:
@genericno longer takes a master definition. Methods (in the generic function sense) are registered directly with@generic; the first method definition implicitly creates the generic function.
Fixed:
- Compatibility with Pythons 3.4, 3.5 and 3.7, thanks to a newly set up CI workflow for automated multi-version testing. Also test coverage (statement coverage) is measured by the workflow.
- Significantly improved test coverage, from 85% to 92%. See #68. Many small bugs fixed.
- PyPy3 support: fixed crash in querying the arity of builtin functions. See #67.
- Condition system:
with handlerscatches also derived types, e.g. a handler forExceptionnow catches a signaledValueError.signal(SomeExceptionClass)now implicitly creates an instance with no arguments, just likeraisedoes.- Conditions can now inherit from
BaseException, not only fromException.
mogrifynow skipsnil, actually making it useful for processinglllinked lists. Although this is technically a breaking change, the original behavior was broken, so it should not affect any existing code.
0.14.2 7 August 2020 - "Greenspun" edition:
With the arrival of conditions and restarts, and a REPL server, I think it is now fair to say unpythonic contains an ad-hoc, informally-specified, slow implementation of half of Common Lisp. To avoid bug-ridden, we have tests - but it's not entirely impossible for some to have slipped through. If you find one, please file an issue in the tracker.
This release welcomes the first external contribution! Thanks to @aisha-w for the much improved organization and presentation of the documentation!
Language version:
We target Python 3.6. Now we test on both CPython and PyPy3.
Rumors of the demise of Python 3.4 support are exaggerated. While the testing of unpythonic has moved to 3.6, there neither is nor will there be any active effort to intentionally drop 3.4 support until unpythonic reaches 0.15.0.
That is, support for 3.4 will likely be dropped with the arrival of the next batch of breaking changes. The current plan is visible in the roadmap as the 0.15.0 milestone.
If you're still stuck on 3.4 and find something in the latest unpythonic 0.14.x doesn't work there, please file an issue. (Support for 0.14.x will end once 0.15 is released, but not before.)
New:
- Improve organization and presentation of documentation (#28).
- Macro README: Emacs syntax highlighting for
unpythonic.syntaxand MacroPy. - Resumable exceptions, a.k.a. conditions and restarts. One of the famous killer features of Common Lisp. Drawing inspiration from python-cl-conditions by Alexander Artemenko. See
with restarts(Common Lisp equivalent:RESTART-CASE),with handlers(HANDLER-BIND),signal(SIGNAL),invoke(INVOKE-RESTART). Many convenience forms are also exported; seeunpythonic.conditionsfor a full list. For an introduction to conditions, see Chapter 19 in Practical Common Lisp by Peter Seibel. - REPL server and client. Interactively hot-patch your running Python program! Another of the famous killer features of Common Lisp. The server is daemonic, listening for connections in a background thread. (Don't worry, it's strictly opt-in.) See
unpythonic.net.serverandunpythonic.net.client. - Batteries for network programming:
unpythonic.net.msg: A simplistic message protocol for sending message data over a stream-based transport (such as TCP).unpythonic.net.ptyproxy: Proxy between a Linux PTY and a network socket. Useful for serving terminal utilities over the network. This doesn't usepty.spawn, so Python libraries that expect to run in a terminal are also welcome. Seeunpythonic.net.serverfor a usage example.unpythonic.net.util: Miscellaneous small utilities.
fix: Break infinite recursion cycles (for pure functions). Drawing inspiration from original implementations by Matthew Might and Per Vognsen.- More batteries for itertools:
fixpoint: Arithmetic fixed-point finder (not to be confused withfix).within: Yield items from iterable until successive iterates are close enough (useful with Cauchy sequences).chunked: Split an iterable into constant-length chunks.lastn: Yield the lastnitems from an iterable.pad: Extend iterable to lengthnwith afillvalue.interleave: For example,interleave(['a', 'b', 'c'], ['+', '*']) --> ['a', '+', 'b', '*', 'c']. Interleave items from several iterables, slightly differently fromzip.find: From an iterable, get the first item matching a given predicate. Convenience function.powerset: Compute the power set (set of all subsets) of an iterable. Works also for infinite iterables.CountingIterator: Count how many items have been yielded, as a side effect.slurp: Extract all items from aqueue.Queue(until it is empty) into a list, returning that list.map: Curry-friendly thin wrapper for the builtinmap, making it mandatory to specify at least one iterable.running_minmax,minmax: Extract both min and max in one pass over an iterable. Therunning_variant is a scan and returns a generator; the just-give-me-the-final-result variant is a fold.
ulp: Given a floatx, return the value of the unit in the last place (the "least significant bit"). Atx = 1.0, this is the machine epsilon, by definition of the machine epsilon.partition_int: split a small positive integer, in all possible ways, into smaller integers that sum to it.dynnow supports rebinding, using the assignment syntaxdyn.x = 42. To mass-update atomically, seedyn.update.boxnow supports.set(newvalue)to rebind (returns the new value as a convenience), andunbox(b)to extract contents. Syntactic sugar for rebinding isb << newvalue(wherebis a box).ThreadLocalBox: A box with thread-local contents. It also holds a default object, which is used when a particular thread has not placed any object into the box.Some: An immutable box. Useful for optional fields; tell apart the presence of aNonevalue (Some(None)) from the absence of a value (None).Shim: A shim holds aboxor aThreadLocalBox, and redirects attribute accesses to whatever object is currently in the box. The point is that the object in the box can be replaced with a different one later, while keeping the attribute proxy in place. One use case is to redirect standard output only in particular threads.islicenow supports negative start and stop. (Caution: no negative step; and it must consume the whole iterable to determine where it ends, if at all.)async_raise: Inject KeyboardInterrupt into an arbitrary thread. (CPython only.)resolve_bindings: Get the parameter bindings a given callable would establish if it was called with the given args and kwargs. This is mainly of interest for implementing memoizers, since this allows them to see (e.g.)f(1)andf(a=1)as the same thing fordef f(a): pass.Singleton: a base class for singletons that interacts properly withpickle. The pattern is slightly pythonified; instead of silently returning the same instance, attempting to invoke the constructor while an instance already exists raisesTypeError. This solution separates concerns better; see #22.sym: a lispy symbol type; or in plain English: a lightweight, human-readable, process-wide unique marker, that can be quickly compared to another such marker by object identity (is). These named symbols are interned. Supplying the same name to the constructor results in receiving the same object instance. Symbols survive apickleroundtrip.gensym: a utility to create a new, unique uninterned symbol. Like the pythonic idiomnonce = object(), but with a human-readable label, and withpicklesupport. Object identity of gensyms is determined by an UUID, generated when the symbol is created. Gensyms also survive apickleroundtrip.
Experimental features:
Each experimental feature is a provisional proof-of-concept, usually lacking battle-testing and polish. Details may still change in a backwards-incompatible way, or the whole feature may still be removed. Do not depend on it in production!
- Multiple dispatch. The
genericdecorator makes a generic function with multiple dispatch. Arity and type annotations determine which method of the generic function a specific call of the function is dispatched to.- This essentially allows replacing the
if/elifdynamic type checking boilerplate of polymorphic functions with type annotations on the function parameters, with support for features from thetypingstdlib module. - Inspired by the multi-methods of CLOS (the Common Lisp Object System), and the generic functions of Julia.
- This essentially allows replacing the
typed: The little sister of thegenericdecorator. Restrict allowed argument types to one specific combination only.isoftype: The big sister ofisinstance. Type check a value against a type specification at run time, with support for many (but not all) features from thetypingmodule. This is the machinery that powers@genericand@typed.- If you need a run-time type checker for serious general use, consider the
typeguardlibrary.
- If you need a run-time type checker for serious general use, consider the
Non-breaking changes:
setescape/escapehave been renamedcatch/throw, to match the standard terminology in the Lisp family. The old nonstandard names are now deprecated, and will be removed in 0.15.0.- The parameters of
raisefare now more pythonic, just the objectexcand an optional keyword-onlycause. Old-style parameters are now deprecated, and will be removed in 0.15.0. See #30. runpipeandgetvalueare now both replaced by a single unified nameexitpipe. This is just a rename, with no functionality changes. The old names are now deprecated, and will be removed in 0.15.0.- Accessing the
.xattribute of aboxdirectly is now deprecated. It does not work withThreadLocalBox, which must handle things differently due to implementation reasons. Instead, use the API, which works for both types of boxes.b << newvalue(syntactic sugar) orb.set(newvalue)sends a different object into the box, andunbox(b)(syntactic sugar) orb.get()retrieves the current value. - The
dbg[]macro now works in the REPL, too. See #12. - The
namedlambdablock macro now also names lambdas that are:- Passed as a named argument of a function call, as in
foo(f=lambda ...: ...); or - Inside a dictionary literal, with a literal string key, as in
{"f": lambda ...: ...}. See #40.
- Passed as a named argument of a function call, as in
- Move macro documentation to
doc/macros.md. (Wasmacro_extras/README.md.) - Add contribution guidelines,
HACKING.md.
Fixed:
- Fix initialization crash in
lazyutilif MacroPy is not installed. - Fix bug in
identityandconstwith zero args (#7). - Use standard Python semantics for negative indices (#6).
- Escape continuation analysis in
unpythonic.syntax.utilnow interprets also the literal namethrowas invoking an escape continuation. - Fix pickling of
frozendict(#55). - Fix spurious cache misses in memoizers (#26). The bug affected
memoize,gmemoize,fixandfixtco.
0.14.1 9 June 2019 - Retrofuturistic edition:
Language version:
- Support Python 3.6. First released in 2016, supported until 2021, most distros should have it by now.
- This will be the final release that supports Python 3.4; upstream support for 3.4 ended in March 2019.
New:
Popper, a pop-while iterator.window, a length-n sliding window iterator for general iterables.autoref[]can now be nested.dbg[]now supports also an expression variant, customizable by lexically assigningdbgprint_expr. See the README on macros for details.
Bugfixes:
- Fix crash when SymPy or mpmath are not installed.
mogrifyis now part of the public API, as it should have been all along.- Docs: Mention the
mgfunction in the README.
Non-breaking changes:
- Future-proof
namelambdafor Python 3.8. - Docs:
dbg[]is now listed as a convenience feature in the README.
0.14.0 18 March 2019 - "Dotting the t's and crossing the i's" edition:
Bugfixes:
setup.py: macros are not zip safe, becauseZipImporterfails to return source code for the module and MacroPy needs that.- fix splicing in the
do[]macro;ExpandedDoViewshould now work correctly - fix lambda handling in the
lazifymacro - fix
dict_itemshandling inmogrify(fixes the use of thecurrymacro with code usingfrozendict)
New:
roview: a read-only view into a sequence. Behaves mostly the same asview, but has no__setitem__orreverse.mg: a decorator to mathify a gfunc, so that it willm()the generator instances it makes.- The
do[]macro now supportsdelete[name]to delete a local variable previously created in the same do-expression usinglocal[name << value]. envifyblock macro, to make formal parameters live in an unpythonicenv.autorefblock macro, to implicitly reference attributes of an object (for reading only).
Breaking changes:
- The
macropy3bootstrapper now takes the-moption;macropy3 -m somemod, likepython3 -m somemod. The alternative is to specify a filename positionally;macropy3 somescript.py, likepython3 somescript.py. In either case, the bootstrapper will import the module in a special mode that pretends its__name__ == '__main__', to allow using the pythonic conditional main idiom also in macro-enabled code. - The constructor of the writable
viewnow checks that the input is not read-only (roview, or aSequencethat is not also aMutableSequence) before allowing creation of the writable view. envnow checks finalization status also when deleting attrs (a finalizedenvcannot add or delete bindings)
Non-breaking improvements:
envnow provides also thecollections.abc.MutableMappingAPI.- The
tcomacro now skips nestedcontinuationsblocks (to allow Lispython in Pydialect to supportcontinuations). setup.pynow installs themacropy3bootstrapper.
0.13.1 1 March 2019 - "Maybe a slice?" edition
New:
view: writable, sliceable view into a sequence. Use likeview(lst)[::2]. Can be nested (i.e. sliced again). Any access (read or write) goes through to the original underlying sequence. Can assign a scalar to a slice à la NumPy. Stores slices, not indices; works also if the length of the underlying sequence suddenly changes.islice: slice syntax support foritertools.islice, use likeislice(myiterable)[100:10:2]orislice(myiterable)[42]. (It's essentially a curried function, where the second step uses the subscript syntax instead of the function call syntax.)prod: likesum, but computes the product. A missing battery.iindex: likelist.index, but for iterables. A missing battery. (Makes sense mostly for memoized input.)inn(x, iterable): contains-check (x in iterable) for monotonic infinite iterables, with automatic termination.getattrrec,setattrrec(recursive): access underlying data in an onion of wrappers.primesandfibonaccigenerators, mainly intended for testing and usage examples.SequenceViewandMutableSequenceViewabstract base classes;viewis aMutableSequenceView.
Breaking changes:
- The
fup[]utility macro to functionally update a sequence is gone and has been replaced by thefuputility function, with slightly changed syntax to accommodate. New syntax is likefup(lst)[3:17:2] << values. (This is a two-step curry utilizing the subscript and lshift operators.) ShadowedSequence, and hence alsofupdate, now raise the semantically more appropriateIndexError(instead of the previousValueError) if the replacement sequence is too short.namelambdanow returns a modified copy; the original function object is no longer mutated.
Non-breaking improvements:
ShadowedSequencenow supports slicing (read-only), equality comparison,strandrepr. Out-of-range access to a single item emits a meaningful error, like inlist.envanddynnow provide thecollections.abc.MappingAPI.consand friends:BinaryTreeIteratorandJackOfAllTradesIteratornow support arbitarily deep cons structures.
0.13.0 25 February 2019 - "I'll evaluate this later" edition:
New:
lazifymacro: call-by-need for Python (a.k.a. lazy functions, like in Haskell)frozendict: an immutable dictionarymogrify: in-placemapfor mutable containerstimer: a context manager for performance testings: create lazy mathematical sequences. For example,s(1, ...),s(1, 2, ...),s(1, 2, 4, ...)ands(1, 2, ...)**2are now valid Python. Regular function, no macros.m: endow any iterable with infix math support. (But be aware that after that, applying an operation meant for general iterables drops the math support; to restore it,m(result)again.)- The
unpythonic.llistmodule now providesJackOfAllTradesIteratorthat understands both trees and linked lists (with some compromises). nbmacro: a silly ultralight math notebook.
Breaking changes:
dyn: theasdictanditemsmethods now return a live view.- The mutable single-item container
Boxand its data attributevaluehave been renamed toboxandx, respectively. namedlambdamacro: Env-assignments are now processed lexically, just like regular assignments. Added support for let-bindings.currymacro: The special mode for uninspectables is now enabled lexically within thewith curryblock. Also, manual uses of thecurrydecorator (on bothdefandlambda) are now detected, and in such cases the macro now skips adding thecurrydecorator.
Non-breaking improvements:
namelambdanow supports renaming any function object, and also multiple times.- The single-item special binding syntax is now supported also by the bindings block of the
dlet,dletseq,dletrec,blet,bletseqandbletrecmacros.
0.12.0 9 January 2019 - "Metamagical engineering" edition:
What does "metamagical" mean? To me, it means "going one level beyond magic". There is an ambiguity here: on the one hand, the word might mean "ultramagical" - magic of a higher order - yet on the other hand, the magical thing about magic is that what lies behind it is always nonmagical. That's metamagic for you! --Douglas R. Hofstadter, On Self-Referential Sentences (essay, 1981)
New:
- Alternative, haskelly
letsyntaxlet[((x, 2), (y, 3)) in x + y]andlet[x + y, where((x, 2), (y, 3))]- Supported by all
letforms:let,letseq,letrec,let_syntax,abbrev
- Supported by all
- When making just one binding, can now omit outer parentheses in
let:let(x, 1)[...],let[(x, 1) in ...],let[..., where(x, 1)] unpythonic.misc.Box: the classic rackety single-item mutable container- Many small improvements to documentation
Breaking changes:
- New, perhaps more natural
call_cc[]syntax for continuations, replaces earlierwith bind[...]- Conditional continuation capture with
call_cc[f() if p else None] ccparameter now added implicitly, no need to declare explicitly unless actually needed (reduces visual noise in client code)
- Conditional continuation capture with
- Local variables in a
doare now declared using macro-expr syntaxlocal[x << 42], looks more macropythonic - Silly
(lambda)suffix removed from names of named lambdas (to detect them in client code, it's enough thatisinstance(f, types.LambdaType))
0.11.1 22 November 2018 - "Cleaning up, vol. 2" edition:
Enhancements:
- Create a proper decorator registry for the syntax machinery.
- Can now register priorities for custom decorators to tell the syntax system about their correct ordering (for
sort_lambda_decorators,suggest_decorator_index). - Register priorities for (some of) unpythonic's own decorators using this new system, replacing the old hardcoded decorator registry.
- Now lives in
unpythonic.regutil; used only by the syntax subsystem, but doesn't require MacroPy just to start up.
- Can now register priorities for custom decorators to tell the syntax system about their correct ordering (for
- Try to determine correct insertion index for
trampolinedandcurrydecorators in macros that insert them todecorator_listofFunctionDefnodes (using any already applied known decorators as placement hints, like a programmer would). namedlambda: recognize also decorated lambdas, and calls tocurrywhere the last argument is a lambda (useful forlooped_overet al.).
Breaking change:
- Remove the special jump target
SELF.- Was always a hack; no longer needed now that v0.11.0 introduced the general solution: the
withselffunction that allows a lambda to refer to itself anywhere, not just in ajump. - Now the whole thing is easier to explain, so likely a better idea (ZoP §17, 18).
- Was always a hack; no longer needed now that v0.11.0 introduced the general solution: the
0.11.0 15 November 2018 - "Spring cleaning in winter" edition:
New:
- Add @callwith: freeze arguments, choose function later
- Add withself: allow a lambda to refer to itself
- Add let_syntax: splice code at macro expansion time
- Add quicklambda: block macro to combo our blocks with MacroPy's quick_lambda
- Add debug option to MacroPy bootstrapper
Enhancements:
- prefix macro now works together with let and do
Bugfixes:
- detect TCO'd lambdas correctly (no longer confused by intervening FunctionDef nodes with TCO decorators)
- scoping: detect also names bound by For, Import, Try, With
Breaking changes:
- Rename dynscope --> dynassign; technically dynamic assignment, not scoping
- Rename localdef --> local; shorter, more descriptive
- scanr now returns results in the order computed (CAUTION: different from Haskell)
- simple_let, simple_letseq --> let, letseq in unpythonic.syntax.simplelet
- cons now prints pythonically by default, to allow eval; use .lispyrepr() to get the old output
- Remove separate dynvar curry_toplevel_passthrough; expose curry_context instead
Other:
- Reorganize source tree, tests now live inside the project
- Pythonize runtests, no more bash script
- Add countlines Python script to estimate project size
0.10.4 29 October 2018 - "573 combo!" edition*:
- new: macro wrappers for the let decorators
- fix: trampolined() should go on the outside even if the client code manually uses curry()
- enh: improve tco, fploop combo
- enh: improve lexical scoping support
0.10.3 25 October 2018 - "Small fixes" edition:
- enh:
currymacro now curries also definitions (def,lambda), not only calls - fix: spurious recomputation bug in
do[] - update and fix READMEs and docstrings
0.10.2 24 October 2018 - "Just a few more things" edition:
Bugfixes:
- Arities:
- Compute arities of methods correctly
- Workaround for some builtins being uninspectable or reporting incorrect arities
- Macros:
- An implicit
curry(f)should callfalso if no args - Fix missing
optional_varsin manually createdwithitemnodes
- An implicit
0.10.1 23 October 2018 - "Just one more thing" edition:
continuations: create continuation using same node type (FunctionDeforAsyncFunctionDef) as its parent functionautoreturn: fix semantics of try block- fix docstring of tco
0.10.0 23 October 2018 - "0.10.0 is more than 0.9.∞" edition:
- Add more macros, notably
continuations,tco,autoreturn - Polish macros, especially their interaction
- Remove old exception-based TCO, rename
fasttcototco
0.9.2 9 October 2018 - "Through the looking glass" edition:
- new
multilambdablock macro: supercharge regular Python lambdas, contained lexically inside the block, with support for multiple expressions and local variables. Use brackets to denote a multi-expression body. - new
fupmacro providing more natural syntax for functional updates; allows using slice syntax. - upgrade: the
letmacros can now optionally have a multi-expression body. To enable, wrap the body in an extra set of brackets. - remove the 0.9.0 multilambda
λ; brittle and was missing features.
The macros implement the multi-expression bodies by inserting a do; this introduces an internal-definition context for local variables. See its documentation in the macro_extras README for usage.
The macro_extras README now includes a table of contents for easy browsability.
0.9.0 5 October 2018 - "Super Syntactic Fortress MACROS" edition:
- Macros! New module
unpythonic.syntax, adding syntactic macros for constructs where this improves usability. Seemacro_extrasfor documentation.- Notable macros include
curry(automatic currying for Python) andcond(multi-branch conditional expression, usable in a lambda), and macro variants of theletconstructs (no boilerplate). - As of this writing, requires the latest MacroPy3 from git HEAD.
- Not loaded by default. To use,
from unpythonic.syntax import macros, ....
- Notable macros include
- Include generic MacroPy3 bootstrapper for convenience, to run macro-enabled Python programs.
- Fix bug in let constructs: should require unique names in the same
let/letrec. - Fix bug in
unpythonic.fun.apply.
0.8.8 25 September 2018 - "More spicy" edition:
Changes:
curry: by default,TypeErrorif args remaining when exiting top-level curry context- add dynvar
curry_toplevel_passthroughto switch the error off
- add dynvar
rotatenow conceptually shifts the arg slots, not the values; this variant seems easier to reason about.- accept just tuple (not list) as the pythonic multiple-return-values thing in
curry,compose,pipe
New:
- add
make_dynvar, to set a default value for a dynamic variable. Eliminates the need forif 'x' in dynchecks. - as an optional extra, add a MacroPy3 based autocurry macro, which automatically curries all function calls that lexically reside in a
with curryblock. (Make your Python look somewhat like Haskell.)
Bugfixes/optimizations:
nth: fix off-by-one bugdyn: skip pushing/popping a scope if no bindings given
0.8.7 24 September 2018 - "More iterable" edition:
Changes:
scanrnow syncs the left ends of multiple inputs, as it should.- robustness: add a typecheck to
ShadowedSequence.__init__ - rename: for consistency with
rscanl,rfoldl, the new names of sync right ends of multiple inputs, then map/zip from the right, arermap,rzip. llist,lreverseare now more ducky/pythonic.
New:
- add
rscanl,rscanl1,rfoldl,rreducel: reverse each input, then left-scan/left-fold. This approach syncs the right ends if multiple inputs. - add
mapr,zipr(map-then-reverse): sync left ends of multiple inputs, then map/zip from the right. - add convenience function
rev: tryreversed(...), ifTypeErrorthenreversed(tuple(...)) - add
butlast,butlastn,partition
0.8.6 20 September 2018 - "Adding in the missing parts" edition:
New features:
- add
unfold,unfold1: generate a sequence corecursively - add memoization for iterables (
imemoize,fimemoize)
Enhancements:
callnow accepts also args (see docstring)- gtco: allow tail-chaining into any iterable
- document new features in README (also those from 0.8.5)
Bugfixes:
- fix bugs in gtco
- strip trampolines correctly in nested generator-TCO chains
- fix handling of generator return values
0.8.5 19 September 2018 - "Liberté, égalité, fraternité" edition:
- add
gtrampolined: TCO (tail chaining) for generators - add
gmemoize: memoization for generators - bring convenience features of
dynto parity withenv
0.8.4 18 September 2018 - "The hunt for missing operators" edition:
- Parameterize scan and fold; can now terminate on longest input
- Add
map_longest,mapr_longest,zipr_longest unpackis now curry-friendly
Technical enhancements:
- refactor
unpythonic.itto useitertoolswhere possible - remove unnecessary conversions from iterator to generator
0.8.3 18 September 2018 - "I have always wanted to code in Listhonkell" edition:
- Add
scanl,scanr: lazy partial fold (a.k.a. accumulate) that returns a generator yielding intermediate results.- Also provided are
scanl1,scanr1variants with one input sequence and optional init.
- Also provided are
- Add
iterate: return an infinite generator yieldingx,f(x),f(f(x)), ...- 1-in-1-out (
iterate1) and n-in-n-out (iterate) variants are provided. The n-in-n-out variant unpacks each result to the argument list of the next call.
- 1-in-1-out (
For usage examples see test() in it.py.
0.8.2 17 September 2018
New features:
- Add currying compose functions and currying pipe (names suffixed with
c)
Enhancements:
- Improve reversing support in linked lists:
- Linked lists now support the builtin
reversed(by internally building a reversed copy) llistjust extracts the internal reversed copy when the input isreversed(some_ll)
- Linked lists now support the builtin
- Prevent stacking curried wrappers in
curry - More logical naming for the
composevariants.- The first suffix, if present, is either
1for one-arg variants, orcfor the new currying variants. - The
isuffix for iterable input always goes last.
- The first suffix, if present, is either
0.8.1 14 September 2018
New feature:
- Add a toy nondeterministic evaluator
forall; seeunpythonic.ambmodule
Enhancements:
- Improve curry; see updated examples in README
- cons structures are now pickleable
unpythonic.llistno longer depends onunpythonic.tco
0.8.0 12 September 2018
Features:
unpythonic.it: batteries for itertools (new)unpythonic.fun: batteries for functools (significant changes)- m-in-n-out for pipes and function composition (new)
unpythonic.fup: functionally update sequences and mappings (new)- Load
unpythonic.llistby default (easier, no special cases for the user)
Bugfix:
currywith passthrough: use up all kwargs at the first step which got too many positional arguments (since no simple reasonable way to decide to which later application they would belong)
0.7.0 4 September 2018
- Add batteries for functools:
unpythonic.fun - Add
consand friends:unpythonic.llist(not loaded by default; depends onunpythonic.tco) - Bugfix:
rc.pywas missing from the distribution, breaking TCO
0.6.1 29 August 2018 (hotfix for 0.6.0)
Bugfix:
- Catch
UnknownArityin all modules that useunpythonic.arity.arity_includes().
0.6.0 29 August 2018
New and improved sequencing constructs.
- Rename the v0.5.1
do-->pipe - Add
piped,lazy_pipedfor shell-like pipe syntax - Add a new
do: an improvedbeginthat can name intermediate results
0.5.1 13 August 2018
- Catch more errors in client code:
- Validate arity of callable value in
letrec(both versions) - Validate callability and arity of body in all
letconstructs - In
env, require name to be an identifier even when subscripting
- Validate arity of callable value in
- Add flag to
enable_fasttco()to be able to switch it back off (during the same run of the Python interpreter) - Internal enhancement: add unit tests for
env
0.5.0 10 August 2018
- Make the TCO implementation switchable at run time, see
enable_fasttco(). - Default to the slower exception-based TCO that has easier syntax.
0.4.3 9 August 2018
- Print a warning for unclaimed TCO jump instances (to help detect bugs in client code)
0.4.2 6 August 2018 (hotfix for 0.4.1)
- fix install bug
0.4.1 6 August 2018 (hotfix for 0.4.0)
- Export
unpythonic.misc.pack - Update description for PyPI
0.4.0 6 August 2018
- First version published on PyPI
- Add
@breakably_looped,@breakably_looped_over - Rename
@immediateto@call; maybe the most descriptive short name givencall/ecand similar.
0.3.0 6 August 2018
- refactor looping constructs into
unpythonic.fploop - add exception-based alternative TCO implementation
- add
raisef - change API of
escapeto perform theraise(simplifies trampoline, improves feature orthogonality)
0.2.0 3 August 2018
- add
@call/ec - add
arityutilities - rename
@loop->@looped; nowloopis the magic first positional parameter for the loop body - add
@looped_over - unify
letbehavior between the two implementations - make
@trampolinedpreserve docstrings @setescape: parameter should be namedtagssince it accepts a tuple- improve README
0.1.0 30 July 2018
Initial release.