Skip to content

Commit 1770be7

Browse files
committed
improve "what belongs in Python?"
1 parent e4e2313 commit 1770be7

File tree

1 file changed

+13
-6
lines changed

1 file changed

+13
-6
lines changed

doc/design-notes.md

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -82,20 +82,27 @@ If you feel [my hovercraft is full of eels](http://stupidpythonideas.blogspot.co
8282

8383
Some have expressed the opinion [the statement-vs-expression dichotomy is a feature](http://stupidpythonideas.blogspot.com/2015/01/statements-and-expressions.html). The BDFL himself has famously stated that TCO has no place in Python [[1]](http://neopythonic.blogspot.com/2009/04/tail-recursion-elimination.html) [[2]](http://neopythonic.blogspot.fi/2009/04/final-words-on-tail-calls.html), and less famously that multi-expression lambdas or continuations have no place in Python [[3]](https://www.artima.com/weblogs/viewpost.jsp?thread=147358). Several potentially interesting PEPs have been deferred [[1]](https://www.python.org/dev/peps/pep-3150/) [[2]](https://www.python.org/dev/peps/pep-0403/) or rejected [[3]](https://www.python.org/dev/peps/pep-0511/) [[4]](https://www.python.org/dev/peps/pep-0463/) [[5]](https://www.python.org/dev/peps/pep-0472/).
8484

85-
In general, I like Python, and my hat's off to the devs. It's no mean feat to create a high-level language that focuses on readability and approachability, keep it alive for 30 years and counting, and have a large part of the programming community adopt it. But regarding the above particular points, if I agreed, I wouldn't be doing this, or [`mcpyrate`](https://github.com/Technologicat/mcpyrate) either. I think that with macros, Python can be so much more than just a beginner's language, and that language-level extensibility is the logical endpoint of that.
85+
In general, I like Python, and my hat's off to the devs. It's no mean feat to create a high-level language that focuses on readability and approachability, keep it alive for 30 years and counting, and have a large part of the programming community adopt it. But regarding the particular points above, if I agreed, I wouldn't be doing this, or [`mcpyrate`](https://github.com/Technologicat/mcpyrate) either.
8686

87-
Of the particular points, in my opinion TCO should at least be an option. I like that *by default*, Python will complain about a call stack overflow rather than hanging when entering an accidentally infinite mutual recursion. But sometimes, I'd like to enable TCO selectively. If you ask for it, you know what to expect. Well, `unpythonic.syntax` has `with tco` precisely for that.
87+
I think that with macros, Python can be so much more than just a beginner's language, and that language-level extensibility is the logical endpoint of that. I don't get the sentiment against metaprogramming, or toward some language-level features. For me, macros (and full-module transforms a.k.a. dialects) are just another tool for creating abstractions, at yet another level. We can already extract procedures, methods, and classes. Why limit that ability - namely, the ability to create abstractions - to what an [eager](https://en.wikipedia.org/wiki/Evaluation_strategy#Strict_evaluation) language can express at run time? If the point is to keep code understandable, then it's a matter of education. It's perfectly possible to write unreadable code without macros, and in Python, no less. And it's perfectly possible to write readable code with macros. I'm willing to admit the technical objection that *macros don't compose*; but that doesn't make them useless.
8888

89-
I think multi-expression `lambda` is, on the surface, a good idea, but really the issue is that the `lambda` construct is broken. We would be much better off if `def` was an expression. Most of the time, anonymous functions aren't such a great idea, but defining closures inline is - and sometimes, the obvious solution is to do that in an expression position. The convenience is similar to being able to nest `def` statements, an ability Python already has.
89+
Of the particular points above, in my opinion TCO should at least be an option. I like that *by default*, Python will complain about a call stack overflow rather than hang, when entering an accidentally infinite mutual recursion. I do occasionally make such mistakes when developing complex algorithms. But sometimes, I'd like to enable TCO selectively. If you ask for it, you know what to expect. This is precisely why `unpythonic.syntax` has `with tco`. I'm not very happy with having a custom TCO layer on top of a language core that doesn't like the idea, because TCO support in the core (like Scheme and Racket have) would simplify the implementation of certain other language extensions; but then again, [this is exactly what Clojure did](https://clojuredocs.org/clojure.core/trampoline), too.
9090

91-
The macros in `unpythonic.syntax` inject lots of lambdas, because that makes them much simpler to implement than if we had to always lift a `def` into the nearest enclosing statement context. Another case in point is [`pampy`](https://github.com/santinic/pampy). The code to perform a pattern match would read a lot nicer if you could define also slightly more complex actions inline. It's unlikely you'll need the action functions elsewhere, and it's just silly to define a bunch of functions *before* the call to `match`. If this isn't a job for either something like `let-where` (to invert the presentation order locally) or multi-expression lambdas (to define the actions inline), I don't know what is.
91+
I think a multi-expression `lambda` is, on the surface, a good idea, but really the issue is that Python's `lambda` construct itself is broken. It's essentially a duplicate of `def`, but lacking some features. We would be much better off if `def` was an expression. Much of the time, anonymous functions aren't such a great idea, but defining closures inline is - and sometimes, the most readily understandable presentation order for an algorithm requires to do that in an expression position. The convenience is similar to being able to nest `def` statements, an ability Python already has. (Also, why are lambdas strictly anonymous? In cases where it is useful to be able to omit a name (because sometimes there are many small helpers and [naming is hard](https://martinfowler.com/bliki/TwoHardThings.html)), why not include the source location information in the auto-generated name, instead of just `"<lambda>"`?)
9292

93-
True multi-shot continuations... `unpythonic.syntax` has `with continuations` precisely for that, but I'm not sure if I'll ever use it in production code. However, it's something that's great to have for teaching the concept in a programming course, when teaching in Python. For everyday use, one-shot continuations (a.k.a. resumable functions, a.k.a. Python's generators) are often all that's needed to simplify certain patterns, especially those involving backtracking. I'm a big fan of the idea that, for example, you can make your anagram-making algorithm only yield valid anagrams, with the backtracking state (to eliminate dead-ends) implicitly stored in the paused generator!
93+
The macros in `unpythonic.syntax` inject lots of lambdas, because that makes them much simpler to implement than if we had to always lift a `def` statement into the nearest enclosing statement context. Another case in point is [`pampy`](https://github.com/santinic/pampy). The code to perform a pattern match would read a lot nicer if you could define also slightly more complex actions inline (see [Racket's pattern matcher](https://docs.racket-lang.org/reference/match.html) for a comparison). It's unlikely you'll need the action functions elsewhere, and it's just silly to define a bunch of functions *before* the call to `match`. If this isn't a job for either something like `let-where` (to invert the presentation order locally) or multi-expression lambdas (to define the actions inline), I don't know what is.
9494

95-
On a point raised [here](https://www.artima.com/weblogs/viewpost.jsp?thread=147358) with respect to indentation-sensitive vs. indentation-insensitive parser modes, having seen [SRFI-110: Sweet-expressions (t-expressions)](https://srfi.schemers.org/srfi-110/srfi-110.html), I think Python is confusing matters by linking the mode to statements vs. expressions. A workable solution is to make *everything* support both modes (or even preprocess the source code text to use only one of the modes), which *uniformly* makes parentheses an alternative syntax for grouping.
95+
On a point raised [here](https://www.artima.com/weblogs/viewpost.jsp?thread=147358) with respect to indentation-sensitive vs. indentation-insensitive parser modes, having seen [SRFI-110: Sweet-expressions (t-expressions)](https://srfi.schemers.org/srfi-110/srfi-110.html), I think Python is confusing matters by linking the parser mode to statements vs. expressions. A workable solution is to make *everything* support both modes (or even preprocess the source code text to use only one of the modes), which *uniformly* makes parentheses an alternative syntax for grouping.
9696

9797
It would be nice to be able to use indentation to structure expressions to improve their readability, like one can do in Racket with [sweet](https://docs.racket-lang.org/sweet/), but I suppose ``lambda x: [expr0, expr1, ...]`` will have to do for a multi-expression lambda. Unless I decide at some point to make a source filter for [`mcpyrate`](https://github.com/Technologicat/mcpyrate) to auto-convert between indentation and parentheses; but for Python this is somewhat difficult to do, because statements **must** use indentation whereas expressions **must** use parentheses, and this must be done before we can invoke the standard parser to produce an AST. (And I don't want to maintain a [Pyparsing](https://github.com/pyparsing/pyparsing) grammar to parse a modified version of Python.)
9898

99+
As for true multi-shot continuations... `unpythonic.syntax` has `with continuations` for that, but I'm not sure if I'll ever use it in production code. Most of the time, it seems to me full continuations are a solution looking for a problem. However, the feature is great to have for teaching the concept of continuations in a programming course, when teaching in Python. For everyday use, one-shot continuations (a.k.a. resumable functions, a.k.a. Python's generators) are often all that's needed to simplify certain patterns, especially those involving backtracking. I'm a big fan of the idea that, for example, you can make your anagram-making algorithm only yield valid anagrams, with the backtracking state (to eliminate dead-ends) implicitly stored in the paused generator!
100+
101+
Finally, how about subtly incompatible Python-like languages (see the rejected [PEP 511](https://www.python.org/dev/peps/pep-0511/))? It is pretty much the point of language-level extensibility, to allow users to do that if they want. I wouldn't worry about it. Racket is *designed* for extensibility, and its community seems to be doing just fine - they even *encourage* the creation of new languages to solve problems. On the other hand, Racket demands some sophistication on the part of its users, and it is not very popular; it's hard to say what the programming community at large would do with an extensible language.
102+
103+
What I can say is, `unpythonic` is not meant for the average Python project, either. But if used intelligently, it can make your code shorter, yet readable. Obviously, in a large project with a high developer turnover, the optimal solution looks different.
104+
105+
99106
## Killer features of Common Lisp
100107

101108
In my opinion, Common Lisp has three legendary killer features:

0 commit comments

Comments
 (0)