This idea comes from a functional pearl called "Power Series, Power Serious" [0], which is well worth reading.<p>I implemented the same thing myself in F#. [1]<p>[0]: <a href="https://citeseerx.ist.psu.edu/document?repid=rep1&type=pdf&doi=4666d43d39c890cfe88630eb93e39afe3110f930" rel="nofollow">https://citeseerx.ist.psu.edu/document?repid=rep1&type=pdf&d...</a><p>[1]: <a href="https://github.com/brianberns/PowerSeries">https://github.com/brianberns/PowerSeries</a>
Absolutely unrelated, but there’s a Haskell-like syntax for Python: <a href="https://web.archive.org/web/20241205024857/https://pyos.github.io/dg/" rel="nofollow">https://web.archive.org/web/20241205024857/https://pyos.gith...</a><p><pre><code> f = x -> raise if
x :: int => IndexError x
otherwise => ValueError x
</code></pre>
Complete with pipelines, of course:<p><pre><code> "> {}: {}".format "Author" "stop using stale memes"
|> print</code></pre>
I really like this idea too. Generators are one of my favorite parts of Python — super memory efficient, and great for chaining transformations. But in practice, I’ve found they can get hard to reason about, especially when you defer evaluation too much. Debugging gets tricky because you can’t easily inspect intermediate states.<p>When working with other engineers, I’ve learned to be careful: sometimes it’s better to just materialize things into a list for clarity, even if it’s less “elegant” on paper.<p>There’s a real balance between cleverness and maintainability here.
> The $ operator is nothing but syntactic sugar that allows you to write bar $ foo data instead of having to write bar (foo data). That’s it.<p>Actually, it's even simpler than that: the $ operator is nothing but a function that applies its left argument to its right one! The full definition is<p><pre><code> f $ x = f x
</code></pre>
(plus a directive that sets its precedence and association)
Well, definitely very cool, but: the Haskell code is delightfully readable while the Python code takes effort for me to read. This is not meant as a criticism, this article is a neat thought experiment.
Generators are one of my favorite features of Python when used in this way. You can assemble complex transformation pipelines that don’t do any actual work and save it all for one single materialization.
I've never written a line of python in my life, so I'm interested in how this "recursive function" can do anything different on each recursive call if it takes no arguments?<p><pre><code> def ints():
yield 1
yield from map(lambda x: x + 1, ints())
</code></pre>
Surely it would always yield a stream of `1`s? Seems very weird to my brain. "As simple as that" it is not!
This is certainly neat. This isn't a criticism, but I think more like an expansion on the author's point:<p>The reason this all works is that generators plus memoization is "just" an implementation of the lazy sequences that Haskell has built in.
I have no idea why this is even slightly neat? Like, it's not surprising that it works, it's not clever, it's not performant, and you can't actually code like this because it will overflow the stack pretty quickly. It doesn't even save lines of code.<p>Alternatives that aren't dumb:<p><pre><code> for x in range(2**256): # not infinite but go ahead and run it to the end and get back to me
from itertools import repeat
for x, _ in enumerate(repeat(None)): # slightly more annoying but does work infinitely
</code></pre>
Granted these aren't clever or non-performant enough to excite functional code fanboys.
This is one of the reasons I hate python, it allows for so many weird things that are only borderline standardised, if at all. When I started with python decades ago, I was also all hype, list comprehensions and functional patterns everywhere, and why not monkey patch a third party library on runtime?
Now I regularly see such code written by the new junior devs, painful to maintain, painful to integrate into a larger code base, most certainly failing with TorchScript, and plainly convoluted for the sake of appearing smart.<p>If you want to code it Haskell, have you considered using Haskell?