Skip to content(if available)orjump to list(if available)

Haskelling My Python

Haskelling My Python

35 comments

·April 18, 2025

notpushkin

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

agumonkey

not too far from that there's https://coconut-lang.org/

sizeofchar

Wow, this is so awesome! A shame it didn’t progress.

brianberns

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

abeppu

Huh, there must have been something in the water leading up to this. Also from 1998 is this paper, "Calculus in coinductive form" and neither of these cites the other. https://ieeexplore.ieee.org/document/705675

barrenko

I was introduced to the notion of power series two weeks ago, and now it's seemingly everywhere...

cartoffal

> 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)

IshKebab

This kind of thing is emblematic of how little Haskellers care about readability and discoverability. I'm sure it's great for people that are already expert Haskellers but it adds yet another thing to learn (that's really hard to search for!) and the benefit is... you can skip a couple of brackets. Awesome.

louthy

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!

lalaithion

The first item yielded from ints() is 1.

For the second item, we grab the first item from ints(), and then apply the map operation, and 1+1 is 2.

For the third item, we grab the second item from ints(), and then apply the map operation, and 1+2 is 3.

louthy

Got it, thanks! The syntax was throwing me a bit there.

lalaithion

It’s a pretty bad system since it takes O(n^2) time to produce n integers but ¯\_(ツ)_/¯. Haskell avoids the extra cost by immutable-self-reference instead of creating a new generator each time.

rowanG077

Sure it yields 1. But then it adds one to each yield form the recursive call. And repeat.

sanderjd

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.

gerad

Isn’t the original python without the recursion lazy as well? I that was the entire point of generators.

mark_l_watson

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.

nurettin

Excited to read their next blog where they discover functools.partial and return self.

whalesalad

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.

ltbarcly3

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.

mrkeen

The functional fanboys have moved onto languages that don't overflow the stack.

hedora

TIL haskell is Turing complete but only uses bounded memory.

ltbarcly3

Read the article?

nexo-v1

[dead]

BiteCode_dev

Or, you know, use numpy.

fp64

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?

sanderjd

There is nothing weird or "borderline standardized" about generators...

fp64

Fair point, they have a proper PEP. I’ve been burnt by `lst[::-1]` and the likes which TorchScript does not (did not?) support and to my surprise learned that the standard is not clear on this behaviour. Generators are fine but I somewhat fail to see their use as they move state somewhere not really clear and I have seen plenty of problems with needing `list(generator())` confusing people

ltbarcly3

I wrote a whole response thinking you were like 19 but you say you started with Python decades ago. If you still haven't gotten the hang of it I don't think there's anything to say. Generators have been in python for more than 2 decades so I find your claims extremely suspect.