if foo.is_success():
...
else: # foo.is_failure()
...
Then you're back to branching on booleans. And we haven't even addressed the “lack of exhaustiveness checks” objection.People are often satisfied by the veneer of something better, because, they were told it was better but don't truly understand why. This is super common in the programming world, I'm afraid.
(0) Functional programming is expressing computation as the evaluation of functions. A language is “functional” to the extent it allows you to do functional programming.
(1) A function is a mapping from values to values. To express computation as the evaluation of functions, you need a rich universe of values: not just primitive ones like integers and object references, but also tuples, lists, trees, etc.
(2) Values are timeless entities that are distinguished from each other structurally. For example, it makes no sense to distinguish “this list [1,2,3]” from “that list [1,2,3]”, because both have the same structure - the same parts. Thus, what Python calls “tuples” and “lists” are not really tuple values and list values.
---
@_9jgl
Let's see how equal they remain in a timeless fashion:
xs = [1,2,3]
ys = [1,2,3]
zs = ys
send_to_another_thread(xs,ys,zs)
print(xs == ys) # who knows
print(ys == zs) # True
print(xs is ys) # False
print(ys is zs) # True
Turns out, the real equality testing operator in Python is `is`, not `==`. The only values Python gives you are object references.---
@lmm
> This is by no means the only way to write Python
This is not the problem. The problem is that Python doesn't have compound values. Values are a matter of semantics, and semantics is a matter of how the language is defined, not how you choose to program in it.
Would you mind expanding on this? I'm not sure I really understand what you mean – if we have two lists a = [1,2,3] and b = [1,2,3], then a == b, so I don't see how Python is distinguishing them from each other.
{ok, integer()} | {err, string()}Edit: although I guess that just using a tuple would be better for this maybe
I don't see what encapsulation you're talking about. Maybe and Either are very much concrete, not abstract, data types.
> Where some situation means that null already has a meaning, the alternative is to create a wrapper class for your situation (not general), or a general wrapper class which is basically what this is.
Unless you want to take advantage of existing plumbing infrastructure such as a monad transformer library, custom wrapper classes for your particular situation are precisely the right tool for the job.
By itself you might as well use nulls or throw errors although you could argue that it's clearer having a maybe or an either.
The monad laws already fail miserably in Haskell when you account for nontermination. Haskellers can only cope with this by eschewing partial functions as a matter of coding style. Now, eschewing partial functions, not to mention effectful procedures, is essentially impossible in a unityped language like Python. Are you sure you still want to play the monad game?
Of course, the decision to tolerate leaky abstractions is very much subjective. But constantly dealing with abstraction leaks is, in my experience, far more burdensome than not using abstractions to begin with. This is especially the case with mathematical abstractions whose meaning is given by equational / rewriting rules.
foo.fold(lambda successResult: ..., lambda failResult: ...)
Even without that, returning a "foo" that has to be explicitly unwrapped makes for a safer API than having "foo" be sometimes a valid result and sometimes silently an error instead.Do you seriously consider this less of a hassle than branching on a frigging bit?