-
Notifications
You must be signed in to change notification settings - Fork 3
Separate documentation files. #28
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
In order to reduce the size of README.md, I moved some documentation to their own files, and updated README.md with appropriate navigation.
Fixed formatting.
|
Hi, and welcome to GitHub! This looks good. I see you also made some formatting and grammar corrections, which is nice. Optionally, if you want, there are some design notes also in the macro readme, which could be moved into Also, when reviewing the changes, I noticed that maybe it would be better if the silly attempts at Python humor (eels etc.) in the original text were moved out of the headings and into the main text, but technically that's an issue separate from the actual splitting. Basically, to improve usability, the heading being jumped to should match the text visible on the link. If you feel this is already enough, I can merge the PR as-is - just post a comment here to tell me which option you prefer. |
|
Hello! And thanks. |
|
Thanks for the updates, looking good! I wasn't aware GitHub-flavored Markdown supported expandable sections - that's exactly what this documentation needed. Nice! I see you've also removed the added/changed notes, which is good - we can tick off another item from the TODO list in #2. Actually, I'm thinking that since the pace of development has slowed somewhat, and with the polishing of the documentation, the next release (currently codenamed 0.14.2) could be called 1.0. Then it would be possible to start using semantic versioning. Tell me when you're ready with the changes, I'll review and merge then. |
|
Thank you! And apologies for the commit storm, I'm still getting used to contributing. So far I've looked through the macros document and the design notes, but I'd like to go through the pure-python document (and everything else one more time) before finally merging. Thank you for your patience! |
|
A commit storm is a regular day in a git repo... :) Ok, I'll wait until you say it's ready. If you want to combine (some of?) the commits into a single commit, this can be done before merging. Look into interactive rebasing, especially how to squash commits. But be careful, it is possible to make mistakes during the process that may permanently erase commits with no way to restore them. I've done it myself using Magit (the git frontend for Emacs; squashing instructions), but it should also be possible from the git command line. Git-scm.com has a section on interactive rebasing. There's also a general explanation of rebasing in their Pro Git book (it's free to read). If there are just a few commits, I usually don't bother squashing them, unless the project maintainer specifically requests it. Different projects on GitHub have different guidelines. Large, popular projects obviously have to be picky to keep their commit history manageable, but for small projects, the commit volume is so low anyway that often it doesn't matter that much. Of course, then there's the viewpoint of semantics - many developers prefer that a commit identifies one meaningful set of changes. This makes it easier to select just those changes for rebasing later, should a need arise e.g. to backport a bugfix or feature onto an earlier version of the codebase. (Then you just pick that one commit and you're done. Otherwise, will have to dig through the commit log to find which ones to take.) But I'll leave the choice up to you. :) (Magit is great, but perhaps Emacs has a bit too much of a learning curve if you don't need it otherwise.) |
|
I closed the separate TODO issue I opened yesterday for merging the documentation improvements - let's just use this PR to track the status. So don't worry, even though the referring issue above is closed, the improvements are going to be merged into 0.14.2 (or 1.0 as it'll probably be called) :) Looking at the milestone, there's just one more code enhancement to make, after which the final tasks for the upcoming release are to merge your documentation improvements (once complete), and to document the new features and changes. (No hurry - the release schedule is "when it's ready".) Some new functions have been added, and I can write the initial version of the new parts of the documentation. If you want, proofreading is welcome - this would ensure it gets the same QA treatment as the rest - or if you feel that's too much, we can just leave the new parts at my version for now. |
|
I've looked through everything and am fine with the changes for now. I can proofread the new parts of the documentation! I'll wait on standby until you're ready. |
|
Great! The next step is for me to review and merge this PR. It's more convenient to handle the proofreading in a separate PR, so I can easily make my updates on top of the already reformatted documentation. I'll post a comment here (after merging and closing) to let you know when I have the new parts ready. |
|
I looked at the changes and mostly the PR looks great! I have some minor issues for which I'll be requesting changes - tell me if you want to make the changes yourself, or leave them for me to make when I merge. (I'll make a detailed list as soon as I can, and send it as a review here.) One important point I noticed when looking at the changes is that I see now my original documentation may not have been clear enough on the overall layout of The What I had missed to point out clearly is that the pure-Python layer also provides some functionality, which has no macro equivalent. Those parts of the pure-Python layer are intended to be used directly. As someone once put it, macros are the nuclear option of software engineering. In order to keep your design clean and elegant, the general guideline is to avoid writing a macro whenever a regular function could perform the same job. However, there are some jobs regular functions cannot perform. Some language extensions can be extremely inconvenient to use or even impossible to build without macros. This is the role of the macro layer. For example, (Though to be fair, I don't know whether Python needs continuations - Paul Graham happened to provide a recipe in his book, and it was fun to build something similar in Python, so I just went ahead and did it. The general opinion is that continuations are a feature of which much more is written about than it is actually used. Python already has a limited suspend-and-resume facility in the guise of generators and coroutines. It's difficult to come up with an actual use case where the resume multiple times from an already executed point of your code feature of general continuations makes a crucial difference.) As for the Some features are borderline cases. For example, TCO can be used without macros, but you have to remember to decorate each function with At the other extreme, the "batteries" for Having only a soft dependency on the macro system is for both cultural and technical reasons. To most programmers outside the Lisp community, macro systems are unfamiliar, and not understood very well. The title of Greg Hendershott's macro tutorial for Racket I think summarizes the situation well. In the case of Python, there is also the technical aspect that Python was not designed to have macros. Specifically, there are no backward compatibility guarantees regarding its AST representation. In fact, some parts of the AST have changed several times during the 3.x series (see GTS). So in Python, depending on a package that relies on the AST structure may cause your software to break later, when you upgrade to a later minor release of Python (3.x → 3.y), if the macro package happens to go out of support. This includes MacroPy itself, as well as any packages that depend on it. For projects that must remain runnable in the long term with minimal maintenance, this is a concern. So, I think the main points of this should be explained in the documentation, particularly the fact that the pure-Python layer essentially consists of two parts - underlying implementations for macros, and features that are meant to be used directly. Also, it should be marked clearly which is which. I suppose this is marking is something for me to do, since I designed the thing. :) |
Technologicat
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here's the review as promised. The PR looks great!
Just some minor things that we should probably fix before this goes live. See detailed comments.
README.md
Outdated
|
|
||
| This README documents the pure-Python part of ``unpythonic``. See also [documentation for the macros](macro_extras/). | ||
| **This is semantics, not syntax!** |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This section belongs in the macro README, since the macro part is where we add syntaxes.
(Granted, semantically speaking s (mathematical sequences), fup (functional update) and islice (regular slicing syntax for iterables) are borderline cases, but they are regular functions that don't need MacroPy - so, technically, procedures, not syntaxes.)
README.md
Outdated
| - [``setescape``, ``escape``: escape continuations (ec)](#setescape-escape-escape-continuations-ec) | ||
| - [``call_ec``: first-class escape continuations](#call_ec-first-class-escape-continuations), like Racket's ``call/ec``. | ||
| - [``forall``: nondeterministic evaluation](#forall-nondeterministic-evaluation), a tuple comprehension with multiple body expressions. | ||
| [pure-Python (basic implementation) documentation](doc/features.md) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would be clearer to just refer to the "pure-Python part" and "macro part" (or something similar - would you happen to have a better term in mind?), since there are (actually quite many) features that are directly provided by the pure-Python part.
doc/design-notes.md
Outdated
| - ``recur.tco`` in [fn.py](https://github.com/fnpy/fn.py), the original source of the approach used here. | ||
| - [MacroPy](https://github.com/azazel75/macropy) uses an approach similar to ``fn.py``. | ||
|
|
||
| ### Comboability |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could mark explicitly that this section pertains to the macro part. Maybe "Comboability of macros"?
doc/features.md
Outdated
| @@ -0,0 +1,2601 @@ | |||
| # Unpythonic: Python meets Lisp and Haskell | |||
|
|
|||
| Documentation for the underlying pure-Python API, which can be used directly if you don't want to depend on MacroPy. See also [documentation for syntactic macros](macro_extras/). | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Underlying API for macros, correct, but also providing all the features that don't need macros. I think we need some rewording to indicate this.
Maybe something like (feel free to edit):
Documentation for the pure-Python API, which provides any features that need no macros and are meant to be used directly, as well as acts as a core for the macro layer. See also documentation for syntactic macros.
As for features that have both a macro layer and an underlying API, if you don't want to depend on MacroPy, you can use the API directly, but this will be less convenient.
|
|
||
| *This document doubles as the API reference, but despite maintenance on a best-effort basis, may occasionally be out of date at places. In case of conflicts in documentation, believe the unit tests first; specifically the code, not necessarily the comments. Everything else (comments, docstrings and this guide) should agree with the unit tests. So if something fails to work as advertised, check what the tests say - and optionally file an issue on GitHub so that the documentation can be fixed.* | ||
|
|
||
| **This document is up-to-date for v0.14.1.** |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We'll have to remember to update this when I'm done documenting 0.14.2, or 1.0 as it'll be called :)
But yes, it's good to have this note in a central location.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Just to clarify, not requesting any changes to this particular location - just a comment.)
doc/features.md
Outdated
| The values of dynamic variables remain bound for the dynamic extent of the `with` block. Exiting the `with` block then pops the stack. Inner dynamic scopes shadow outer ones. Dynamic variables are seen also by code that is outside the lexical scope where the `with dyn.let` resides. | ||
|
|
||
| <details> | ||
| <summary>Each thread has its own dynamic scope stack. </summary> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe the summary could mention also the global level?
Each thread has its own dynamic scope stack; in addition, there is a global dynamic scope for default values, shared between threads.
doc/features.md
Outdated
|
|
||
| When ``fupdate`` constructs its output, the replacement occurs by walking *the input sequence* left-to-right, and pulling an item from the replacement sequence when the given replacement specification so requires. Hence the replacement sequence is not necessarily accessed left-to-right. (In the last example above, ``tuple(range(5))`` was read in the order ``(4, 3, 2, 1, 0)``.) | ||
|
|
||
| The replacement sequence must have at least as many items as the slice requires (when applied to the original input). Any extra items in t |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could delete these notes on old versions (0.10.0 and 0.11.1).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you mean remove the paragraph?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, could remove the two paragraphs, the one starting with v0.10.0 and the one starting with v0.11.1.
macro_extras/README.md
Outdated
| - Expression-assignment to an unpythonic environment, ``f << (lambda ...: ...)`` | ||
|
|
||
| - Let bindings, ``let[(f, (lambda ...: ...)) in ...]``, using any let syntax supported by unpythonic (here using the haskelly let-in just as an example). | ||
| - Env-assignments are now processed lexically, just like regular assignments. Added support for let-bindings. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some of the wording left over from the old changed-in-version-x note, should be:
- Env-assignments are processed lexically, just like regular assignments.
(Extra "now" in the first sentence. Also, support for let-bindings is already explained above, so it's not needed here.)
macro_extras/README.md
Outdated
| *Changed in v0.13.0.* The special mode for uninspectables used to be enabled for the dynamic extent of the ``with curry`` block; the new lexical behavior is easier to reason about. Also, manual uses of the ``curry`` decorator (on both ``def`` and ``lambda``) are now detected, and in such cases the macro now skips adding the decorator. | ||
| **CAUTION**: Some built-ins are uninspectable or may report their arities incorrectly; in those cases, ``curry`` may fail, occasionally in mysterious ways. The function ``unpythonic.arity.arities``, which ``unpythonic.fun.curry`` internally uses, has a workaround for the inspectability problems of all built-ins in the top-level namespace (as of Python 3.7), but e.g. methods of built-in types are not handled. | ||
|
|
||
| The special mode for uninspectables used to be enabled for the dynamic extent of the ``with curry`` block; the new lexical behavior is easier to reason about. Also, manual uses of the ``curry`` decorator (on both ``def`` and ``lambda``) are now detected, and in such cases the macro now skips adding the decorator. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This paragraph needs new wording now that it is no longer inside a changed-in-version-x note.
Also, the first point of the paragraph is already given further above ("Lexically inside..." ... "...in a special mode that..."), so it can be removed from here. Here we could have just:
Manual uses of the curry decorator (on both def and lambda) are detected, and in such cases the macro skips adding the decorator.
macro_extras/README.md
Outdated
| Lexically inside a ``with prefix`` block, any literal tuple denotes a function call, unless quoted. The first element is the operator, the rest are arguments. | ||
|
|
||
| *Changed in v0.11.0.* Bindings of the ``let`` macros and the top-level tuple in a ``do[]`` are now left alone, but ``prefix`` recurses inside them (in the case of bindings, on each RHS). | ||
| Lexically inside a ``with prefix`` block, any literal tuple denotes a function call, unless quoted. The first element is the operator, the rest are arguments. Bindings of the ``let`` macros and the top-level tuple in a ``do[]`` are now left alone, but ``prefix`` recurses inside them (in the case of bindings, on each RHS). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Misplaced "now" left over from a changed-in-version-x note, should be:
Lexically inside a with prefix block, any literal tuple denotes a function call, unless quoted. The first element is the operator, the rest are arguments. Bindings of the let macros and the top-level tuple in a do[] are left alone, but prefix recurses inside them (in the case of bindings, on each RHS).
|
Ah, one more thing that I didn't yet explain clearly - there are three kinds of features in
Item 3 was missing from the previous explanation. This classification still leaves some sporks - for example, |
|
Thanks for reviewing! I'll look at the changes you've raised, and look into adding the distinction between the three sets of features. |
|
Great! Looking forward to the update! |
9/10 of requested changes made
|
Alright! I've finished up the changes and adding further explanation about the types of features offered. |
|
Great work. Merged! Thanks for your contribution! |
|
Thank you! If you have any more documentation needs, feel free to ping me :-) |
|
Ok, I'll drop you a note here when I have finished the documentation for the new features for 0.14.2. :) |
Moved some of the documentation out of README.md into their own files to improve navigability.
#2