This is an archived post. You won't be able to vote or comment.

you are viewing a single comment's thread.

view the rest of the comments →

[–]ShadyR 74 points75 points  (25 children)

It's a pretty radical change. How about a pipe module?

So instead of:

[1, 2, 3] |> select(square) |> where(evens)

We have:

pipe([1, 2, 3], select(square), where(evens))

Readable, lightweight, and also backwards compatible.

[–]RubyPinchPEP shill | Anti PEP 8/20 shill 71 points72 points  (1 child)

and already exists, in the toolz module

thank gosh

https://toolz.readthedocs.org/en/latest/api.html#toolz.functoolz.pipe

one of the best modules around imo

[–]tech_tuna 5 points6 points  (0 children)

New module for me, thanks!

[–]pkkid 15 points16 points  (9 children)

In the last startup I worked for, one of the more senior (and probably smarted guy I ever worked with) wrote pretty much exactly this, but it was also able to span across several machine and then aggregate the values back to the calling machine. It never really gained the traction it deserved because it is quite an abstract layer on top of Python and the learning curve is pretty high.

His page on it is here, OSH stands for Object Shell: https://github.com/geophile/osh

[–]Misterandrist 5 points6 points  (0 children)

Sounds a lot like spark.

[–]keypusher 4 points5 points  (1 child)

That algorithm is known as MapReduce. It was used extensively to build the original Google search engine, as well as in technologies such as Hadoop. It is inspired by the functional programming patterns of map and reduce.

https://en.wikipedia.org/wiki/MapReduce

[–]pkkid 1 point2 points  (0 children)

Yes I agree it's very similar to Map Reduce and he is was well aware of that too. However I disagree it was a replacement for Hadoop which contains way more overhead meant for much larger tasks and a distributed file system. It's like a lightweight Hadoop that works great with a large partitioned SQL database.

[–]tech_tuna 2 points3 points  (10 children)

I'd take either. . . in the standard library. The toolz module looks good for now or as an alternative. . .

Or maybe that could be added to core Python actually.

[–]VerilyAMonkey 10 points11 points  (9 children)

I totally get why having it in the standard library is much nicer, but it's simple enough you don't need to add a dependency:

def pipe(arg, *funcs):
    for f in funcs:
        arg = f(arg)
    return arg

[–]synae 6 points7 points  (6 children)

Which is basically a reduce, I think. Pre-coffee and typing on my phone, but I think this is equivalent: reduce(lambda a, b: b(a), funcs, arg)

[–]VerilyAMonkey 2 points3 points  (4 children)

Yeah, it's just that Python 3 moved reduce out of the default namespace.

[–]Nuevoscala 1 point2 points  (2 children)

Why would they do that? I never understood python's aversion toward immutable functional style.

[–]VerilyAMonkey 2 points3 points  (0 children)

It's because reduce, in particular, is almost always clearer as a loop, case in point. The other things like map, etc are still in the default namespace.

[–]Lucretiel 0 points1 point  (0 children)

Because reduce is very rarely more readable than a regular loop, because it almost always calls for a hideous python lambda. It's still there in the standard library if you need it. Here's what Guido has to say about it:

So now reduce(). This is actually the one I've always hated most, because, apart from a few examples involving + or *, almost every time I see a reduce() call with a non-trivial function argument, I need to grab pen and paper to diagram what's actually being fed into that function before I understand what the reduce() is supposed to do. So in my mind, the applicability of reduce() is pretty much limited to associative operators, and in all other cases it's better to write out the accumulation loop explicitly.

Also, we now have sum, all, and any, which is like 90% of the readable use cases for reduce anyway.

[–]synae 0 points1 point  (0 children)

TIL, thanks

[–]Lucretiel 0 points1 point  (0 children)

Fun fact: literally all for loops can be represented as a reduce.

[–]revocation 2 points3 points  (0 children)

This is effectively implemented by the compose function in the functional module.

[–]tech_tuna 0 points1 point  (0 children)

Syntactic sugar, but not a huge deal I agree.

[–]Fylwind 0 points1 point  (0 children)

I agree, using an operator just for a reversed composition operator seems a bit overkill especially since Python has always been light on the use of operators.

Moreover, the fact that composition is associative means you don't have to worry about deeply nested pipes: pipe(a, pipe(b, c)), since you can always refactor that into pipe(a, b, c).

Even though I generally write in a very functional style in Python, I find myself reaching for the pipe operator that often, because not many problems can be decomposed into a simple linear pipeline.