April 18th, 2025

Haskelling My Python

The article explains implementing Haskell's lazy infinite lists in Python using generators, covering recursive integer generation, Taylor series for \( e^x \), sine and cosine functions, and memoization for performance.

Read original articleLink Icon
CuriositySkepticismAppreciation
Haskelling My Python

The article discusses the implementation of Haskell's lazy infinite lists in Python using generators. It begins with a simple generator for positive integers, demonstrating how to create it recursively. The author explains the integration of a Taylor series, showcasing how to define the exponential function \( e^x \) using its properties. The article also covers the evaluation of the Taylor series against Python's standard library function for \( e^x \), showing that the results are closely aligned. Additionally, the author introduces the sine and cosine functions through their integral relationships, providing Python implementations for both. To enhance performance, a memoization technique is suggested to address the inefficiencies of Python generators compared to Haskell's lists. The article concludes with a note on using Python's fractions module for rational number outputs.

- The article illustrates how to implement Haskell's lazy infinite lists in Python.

- It demonstrates the creation of a recursive generator for positive integers.

- The integration of Taylor series is explained, particularly for the exponential function \( e^x \).

- Sine and cosine functions are derived using their integral relationships.

- A memoization technique is recommended to improve the performance of Python generators.

AI: What people are saying
The comments on the article about implementing Haskell's lazy infinite lists in Python using generators reveal a mix of appreciation and critique regarding the approach.
  • Several commenters express admiration for the use of generators, highlighting their memory efficiency and ability to create transformation pipelines.
  • There are concerns about the readability and maintainability of the Python code compared to Haskell, with some suggesting that it can become difficult to debug.
  • Some users question the practicality of the recursive function implementation, with one commenter expressing skepticism about its effectiveness.
  • Discussions include references to alternative approaches and languages, such as F# and numpy, indicating a broader context of functional programming.
  • A few comments critique Python's flexibility, suggesting it can lead to convoluted code that is hard to maintain.
Link Icon 14 comments
By @brianberns - 18 days
This idea comes from a functional pearl called "Power Series, Power Serious" [0], which is well worth reading.

I implemented the same thing myself in F#. [1]

[0]: https://citeseerx.ist.psu.edu/document?repid=rep1&type=pdf&d...

[1]: https://github.com/brianberns/PowerSeries

By @notpushkin - 18 days
Absolutely unrelated, but there’s a Haskell-like syntax for Python: https://web.archive.org/web/20241205024857/https://pyos.gith...

  f = x -> raise if
    x :: int  => IndexError x
    otherwise => ValueError x
Complete with pipelines, of course:

  "> {}: {}".format "Author" "stop using stale memes"
    |> print
By @nexo-v1 - 18 days
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.

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.

There’s a real balance between cleverness and maintainability here.

By @cartoffal - 18 days
> 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.

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

    f $ x = f x
(plus a directive that sets its precedence and association)
By @mark_l_watson - 18 days
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.
By @whalesalad - 18 days
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.
By @louthy - 18 days
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?

    def ints():
        yield 1
        yield from map(lambda x: x + 1, ints())
Surely it would always yield a stream of `1`s? Seems very weird to my brain. "As simple as that" it is not!
By @sanderjd - 18 days
This is certainly neat. This isn't a criticism, but I think more like an expansion on the author's point:

The reason this all works is that generators plus memoization is "just" an implementation of the lazy sequences that Haskell has built in.

By @nurettin - 18 days
Excited to read their next blog where they discover functools.partial and return self.
By @benrutter - 18 days
I like this! Tiny question, is the cache at the end any different from the inbuilt functools cache?
By @BiteCode_dev - 18 days
Or, you know, use numpy.
By @ltbarcly3 - 18 days
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.

Alternatives that aren't dumb:

  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
Granted these aren't clever or non-performant enough to excite functional code fanboys.
By @fp64 - 18 days
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.

If you want to code it Haskell, have you considered using Haskell?