> There should be one-- and preferably only one --obvious way to do it
There are already 20 different ways of solving any given problem or of coding any given solution. At least if a single paradigm is used, then those familiar with it are unlikely to run into big surprises about how things are implemented. If your developers are used to an imperative style, a chunk of code in the middle of the codebase written in a strongly functional style is very difficult to comprehend.
In one tool I wrote for work, I use function application to progressively transform a stream of events. It's a beautiful, neat solution to the problem, but confuses the hell out of everyone else because they've never written any Haskell. There are other ways to implement this that are just as clear and abstracted and maybe they take slightly more code, but it is code that is more maintainable by everyone else. Good code to me is not that which looks like it was coded by a genius, but that which looks like any child could have written it. For some code this is very hard, but I find it easier in Python than other languages.
The more 'choices' that you have, the more inconsistency there will be about who chooses what. There is obviously a tradeoff here with giving developers the right tools for the job, but list comprehensions are a great example of how you can do that without having to use a different paradigm.
And then it offers 20 equally good ways: generators vs comprehensions vs iterators vs foreach loops vs map and filter, async vs threadpools vs processpools, and so on.
>> There should be one-- and preferably only one --obvious way to do it
Unfortunately this was true ten years ago. Now, python is just following C++ and become this behemoth of features, especially with recent PEPs, which aim at saving one or two lines of code.
People feel the need for example for anonymous functions that support statements but Python’s lambdas only support expressions and it turns out there’s not much you can do with those in Python. It’s the reason for why over the years Python acquired features for use cases that can normally be solved via higher order functions in other languages like Ruby.
I think it is time to accept Python for what it is. A dirty, mutable language that gets the job done in certain contexts. Trying to change it I think does more harm than good.
Hell, even as you claim, libraries/communities is a chicken and egg problem, and its certainly easier to build a community on a familiar language/paradigm, than a different one.
Its a big ask to assume that it boils down to productivity, especially when most alternatives are completely unknown to most: imperative, oop, and recently functional are the only options non-academics would usually imagine. Concatenative, stack-based, logical, etc are gone from the conversation long before productivity ever comes into play.
At best, the productivity question is likely just a short term one: it costs little up-front to jump from C to Python, compared to C to Haskell. The long-term productivity question is more likely than not, not actually in play. And presumably even haskellers don't think of a proper comparison against C, in the long-term benefits (they'll usually note C's weaknesses against haskell's strengths, but not vice-versa; the C programmers will do the same in C's favor)
Just because an individual or a small team can be highly productive in say, Haskell, doesn't mean it makes sense for the whole company.
In Finance, the area I work in, the only FP success stories that come immediately to mind are a couple of teams at Standard Chartered for Haskell, and Jane Street Capital for OCaml. And they are notable for their rarity, not because there are plenty of other comparable examples.
That said, I'd want a very high concurrence system written in a function language over Python any day (gil notwithstanding). But I'm fine with Python not being that language.
I don't want the extreme strictness of haskell or F# but I'd rather be edging in that direction rather than edging in perl's direction. 572 was definitely the latter.
Zen of Python: There should be one-- and preferably only one --obvious way to do it.
That makes comprehensions the 2nd way to do it. They're also less obvious in my book, since their readability tends to drop precipitously once you start adding multiple "for" clauses to one.
Do not confuse FP with declarative programming or with laziness. FP often implies declarative APIs and laziness but not vice-versa.
(expr for item in iterable if cond) is more or less another way to spell map(lambda item: expr, filter(lambda item: cond, iterable)), except readable.
You could define "functional programming" to absolutely require referential transparency, but that's not what the rest of the thread is doing.
Obviously it's ultimately the same thing either way, but that's the lineage (SETL -> ABC -> Python).
https://docs.python.org/3/howto/functional.html#generator-ex... claims Python borrowed them from Haskell, but the ABC link seems more likely.
As I recall, Python did get them from Haskell, which in turn got them from Miranda, which got them from KRC. Before that it gets fuzzy, but it's likely that SETL was eventually at the root of it.
I think Python is considered anti FP for two reasons: when suggestions to add more FP features have been raised, Guido has fairly unambiguously rejected those features.
That’s all fine though. Python can do what Python wants. All else being equal, I’d prefer a little more functional flavor than Python has, but I won’t turn my nose up at writing Python if duty calls.
However I can barely think of another mainstream and high level language that is more anti-FP than Python.
And lambdas, in spite of popular opinion, have nothing to do with it ;-)
(Actually I can think of another language that is more anti-FP than Python and that would be Go ... people saying Go is “pythonic” have it right)
I've never heard anyone call go pythonic.
JS (which you listed twice), Perl (5 or 6), and in some ways Ruby (which is a mixed bag, because it doesn't actually have functions per se, but even so is in many ways more friendly to functional style than Python.)
Being expression-oriented languages with full support for anonymous functions (callables in Ruby) and non-awkward creation of full (rather than read-only) closures are the main common factors.
I'm not sure what you mean by read only closures, unless you mean a close which can later modify the enclosing scope, which python supports via the nonlocal keyword but also is decidedly bad, and not something I've ever before heard of as an fp concept.
And as far as I can tell, your entire argument reduces to better lambdas. Which is what you said it wouldn't be. I feel misled. Oh wait you're a different user. Still this point stands. I wouldn't have responded if the expected answer was "better lambdas". You can do better.
I guess the next important question is "what are the things you feel make a language functional, and why are those things valuable"?
There are lots of aspects of functional programming that are good, I personally agree that anonymous functions are not one of them, especially when you have no need for callbacks (python doesn't really).
And it's very possible to write functional code, imo, without anonymous functions. Why do you disagree.
I write very few classes in Python. I tend to write modules and import them. It's what I do in Elixir, obviously.
However I don't write much Ruby code outside Rails and Rails is definitely object oriented and directs the developer to write OO code. Instead the most popular web framework for Python, Django, is almost anarchy, anything will do.
Right, Ruby doesn't have functions at all, so you can't pass them. This is why Ruby isn't simply better than Python here, though I'd say it's still mostly better.
> We can pass a symbol with the name of the fuction.
No, you can either convert the name to a callable and pass that, or for some methods you can pass the name of a method you'd like called on an object that the called method will get somewhere else. This isn’t esuivslent to having the name of a function. Instead, its a facility that is usually offered by a method to provide a more concise alternative to passing an equivalent block for a no-arg method. E.g.:
enum.inject(start, :foo)
is equivalent to: enum.inject(start) { |obj| obj.send(:foo) }
If you actually have a function-like (callable) object (bound method object or proc/lambda) you can send that as a regular argument, or if the method it is passed to expects a block (as is more frequently the case for Ruby methods that need a single function-like argument) convert the callable to a block with the “&” prefix and send that.I think they're referring to Symbol#to_proc.
That you need some local state to implement a pure function isn't a problem as you can limit it to very simple things. Anonymous functions are not something you need either, just give it a name and pass it, wtp?
I agree most of the "Python is not functional enough" criticism comes from people who just want as pure FP as possible.
I don't even like the new assignment PEP because it just adds more complexity and more ways of doing the same thing. And knowing that it was the straw that broke the camel's back and pushed Guido away, makes me like it even less.