Returns: Brings functional programming to Python land
returns.readthedocs.io
returns.readthedocs.io
if user is not None:
balance = user.get_balance()
if balance is not None:
credit = balance.credit_amount()
if credit is not None and credit > 0:
discount_program = choose_discount(credit)
I would probably write the python closer to this for readability. Don't judge me too harshly I just rewrote this quickly. if user is None:
return
balance = user.get_balance()
if balance is None:
return
credit = balance.credit_amount()
if credit is not None and credit > 0:
discount_program = choose_discount(credit) credit = user?.get_balance()?.credit_amount()
if credit is not None and credit > 0:
discount_program = choose_discount(credit)Maybe with an additional .get() at the end to unwrap it? Though I imagine a dynamically-created class could avoid that.
there's a PEP. But it's been pocket-vetoed, i think.
My dude. I have yet to see an FP article that is not just badly cherry picked anecdotes, where the cherry picking itself is often somewhere between a misrepresentation and a blatant lie (nobody would ever do <that> but you can if you need to)
It’s one of the reasons I strong advocate against functional programming. The community representation of it is, at best, highly misrepresentative of reality.
I'm not a functional expert, I have only dabbled. I once rewrote a fairly complex imperative Python text processing program in a functional style. It became much clearer. Most of the code was actually reusable higher order functions, and the program logic became very short and essentially declarative.
Why are people so obsessed with None? The explicit check is usually unnecessary. Just check directly.
if user:
if balance:=user.get_balance():
if credit:=balance.credit_amount():
discount_program = choose_discount(credit)[1] https://www.inspiredpython.com/article/truthy-and-falsy-gotc...
As for being obsessed with None. I usually lean towards it when no type has been provided like in the example. I think its appropriate when we are expecting it to be a None just to miss any traps further down the road. Just like typing in Python, you don't have to do it but I find it eliminates issues later on and makes it easier for others to read.
Otherwise railroading (which this sort of thing enables) is pretty powerful. It's just about always ugly, but it's nice at times.
I get that Optional return values can force the programmer to handle errors without resorting to exceptions, but I can't help but wonder whether the first example with all those `bind_optional` ... is just Objective-C's nil messaging in fancy disguise...
FWIW in Objective C this is exactly what one writes (even if those functions can return nil):
credit = user.get_balance().credit_amount()
if credit > 0:
discount_program = choose_discount(credit)
I'm not sure what's so "functional" about it though.This is a library about composing function, can't get more functional than that.
1. Maybe -> Optional[T]
2. Result -> Union[SuccessT, FailureT]
3. Future, FutureResult -> concurrent.futures.Future[T]
4. IO -> don't use this; none of the standard library I/O supports it
I do appreciate the use of "container" as a term instead of "monad".I also appreciate the typechecker enhancements being put forward by their custom plugins.
1. T | None
2. SuccessT | FailureT[0]: https://docs.python.org/3/library/stdtypes.html#types-union
Note that this is not the same concept as an optional argument, which is one that has a default.
[0] https://docs.python.org/3.11/library/stdtypes.html#types-uni...[1] https://docs.python.org/3.11/library/typing.html#typing.Opti...
As a person who teaches functional programming at degree level, this is the kind of thing that would put people off FP before they even get in the door. It is obviously less ergonomic than standard Python, and the code you end up with is no safer or more abstracted than what you started with.
That said, if the authors really do think it's a better way for programming in Python, and it works for them, then more power to 'em.
there are no apis for haskell or other functional langs for data i need.
What’s worse, these versions of ‘FP’ often bear very little resemblance to actual functional programming and its advantages. Rather, people seem to fall into the trap of confusing functional programming with ‘those fancy words I hear from Haskell’. Now, I may like Haskell, but its concepts are only useful because of the rest of the language — you can’t just port ‘monads’ and ‘IO’ into some random language and expect them to be usable. It looks like this library partly avoids that fate, in that it does have more than just ‘monads’… but yes, the monads are still there, and they’re just as clunky as you’d expect from Python.
</rant>
I mean, you can... But only with great discipline and experience. It just (probably) won't work if you're having to mentor more junior engineers with an OOP background.
(Funny, I was downright terrified 15 years ago about ever mentioning "ColdFusion" or "Sharepoint" in a forum because I'd get contacted by recruiters about the first and salespeople about the second. I'd always tell the salespeople that we had no budget at all for Sharepoint and just had one for our dev team because you could get the license for free with an MSDN subscription)
I'm more interested in "pure FP" languages than in multi-paradigm languages, because the former seem much more coherent in design to me. IMO, functional programming isn't about adding extra degrees of freedom (or "extra features"), it's about working within very well-defined and rigid constraints (what one might call safeguards).
optional.py seems to address the same issue and with a slightly better approach (but still ugly).
I love functional programming (use Elixir at work a fair amount), but almost every one of these examples is harder to read than than the equivalent "classic" Python (https://returns.readthedocs.io/en/latest/).
Plus, if you're going to write Python then deviating majorly from the normal approaches is going to require maintainers to learn a whole new way of doing things. Given the tools that Python provides, I don't think that's worth it for the benefit. Perhaps for some specific applications?
By my experience, younger programmers are more likely to want to do this. I fell into this trap myself with a similar library for TypeScript, and discovered the hard way that code written this way was much less readable and debuggable.
Less readable because you're essentially reimplementing variable assignment and control flow with lambdas and bespoke syntax that looks out of place. Less debuggable because the stack traces from these things blow up exponentially, and make it very hard to understand where an error came from.
So yeah, it's not satire, just misguided. If you have to use Python you're much better off sticking to the usual patterns and apply more static analysis on top rather than trying to reinvent the language this way.
However:
- I think the response is excessively negative, there are some really great patterns in here
- I put most of the blame on the ugly "design" of the python language (and a lack of guardrails which takes away a lot of the value a library like this would otherwise provide)
- I can easily imagine this library, or an interface similar to it, being used on a large code base in a production/commercial setting. I think people who can't imagine that probably just don't have a lot of imagination.
In all honesty, Return's docs call out how problematic if-else chaining is, and then proceeds to replace it with chained lambdas. How is that better?
I'd go as far as to say that we can avoid a lot of the null coercion stuff with proper encapsulation, but that is basically saying, "We can solve Python's FP challenges with better object design," and I understand how that may sound like nails on a chalkboard to some.
If this helps you write the kind of code you and your peers want and can understand, who am I to judge?
Simplicity =/= familiarity
I have long been arguing that this is exactly what "readability" means. Readability is completely subjective, and depends on personal experience with various languages, types of syntax and so on.
Code should be judged on its own merit, and in that case your definition of "readability" doesn't really help us go anywhere.
Python is not just a "base" language, it's also a set of patterns for how you go about writing code in it. Conforming to those patterns to some degree makes for (subjectively) nice code, and (objectively) easier to understand code because of the familiarity.
Don't let unfamiliarity get in the way!
I have debugged so much code that does the wrong thing with a Result (or other error handling). I will always remember the engineering manager who boasted about how we (1) did careful error handling with monads and (2) did code reviews. Oddly though I never got my code reviewed and I found so much code that silently dropped errors out of carelessness to believe there had never been code reviews. In my experience given a malfunctioning Scala class you can: (3) screw around with it for a few days or (4) write it in Java and have it working in an hour.
(A counter to this is that sometimes you do want to send a result-or-exception to some other part of the program that involves storing it somewhere and Result is really great for that… but please learn to get over any fear of exceptions you have and ignore the people who want to stigmatize them.)