DEV Community

loading...

Discussion on: Why I don't believe in pure functional programming anymore

johncip profile image
jmc • Edited

Python does have map et al, but lacks the anonymous inner functions which would make them more useful for building & reducing collections.

A few things:

  • Blocks aren't merely the equal of with -- they're more like lambdas where return acts on the outer function. So (like lambdas) they cover the with case, the collection-wrangling cases, and many others.
  • You can pass multiple blocks -- proc makes blocks, lambdas, and even named functions first-class. But the "default block" has more convenient syntax.
  • I don't think it's fair to say that Python has one way to do it (lambdas) while other languages have multiple. I'd say Python has zero, unfortunately. Python's lambdas are anonymous inner expressions while every other language uses the term to mean anonymous inner functions. Likewise, some variants of BASIC have "user-defined functions" which are limited to single statement. Beats not having them, but still not as useful as real functions.

[I edited the above to group them and clarify what I mean by lambdas]

One more example -- there's no "reduce" comprehension in Python. So you're back to loops there. They give you a sum function, but you don't have the thing that would let you write your own sum function elegantly.

I should probably stop... it's hard to talk about these things convincingly because often the problem with Python isn't with what it has, but the fact that it needed to have those things in the first place. Along those lines, yes the generator-style context managers are lighter-weight than classes, but languages with anonymous functions don't need to provide "generator-style context managers" in the first place. Anyway, I don't want to come off as hating Python. It's just behind the times in some ways.

as for vararg functions, I'm not actually sold on them. What's the benefit compared to functions that take arrays?

Not much, since the varargs is syntactic sugar. But it lets you be explicit about intent -- sometimes you really are passing in single argument that happens to be an array. Varargs let you say "this isn't an array, but a bunch of discrete arguments that I will figure out what to do with at runtime."

Thread Thread
yujiri8 profile image
Ryan Westlund Author

One more example -- there's no "reduce" comprehension in Python. So you're back to loops there. For the specific case of adding, they give you a sum function. But you don't have the thing that would let you write your own (at least in a functional style).

Huh? I just mentioned functools.reduce. It works with any function or lambda. What does Ruby's reduce get that Python's doesn't?

Thread Thread
yujiri8 profile image
Ryan Westlund Author

Oh - is it the ability to reduce from the outer function (halting iteration)?

I guess that could be useful... in some really obscure situation or something...

Thread Thread
johncip profile image
jmc • Edited

Oh I just meant Python's comprehensions -- they cover some of the places you'd use map & filter, but there's no comprehension for going down to a scalar, AFAIK. (That said, since reduce can be used to build collections, list / dict / set comprehensions do end up overlapping with it.)

And to be clear, when I said "you're back to loops" I meant for the places where you wouldn't bother to create a named inner function, or to compose named functions before calling them. (It's not that you don't have options, but I'd argue that they're not idiomatic or lightweight.)

What does Ruby's reduce get that Python's doesn't?

While reduce works the same everywhere, it's most useful in languages with anonymous inner functions. It's true of higher-order functions in general, not just reduce.

Ruby goes a step further by providing a shorthand syntax for doing this (but is not unique there -- Clojure has the #() shorthand, and JS has ->).

FWIW, since you mentioned it -- I've only felt the need to halt from inside a reducer function in Clojure. I forget why, but it probably had to do with starting from an infinite stream. It'd be an optimization to prevent the creation of intermediate collections.