Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

The point is that you can indicate whether an operation was successful or not by wrapping that value w/ a Success or Failure, rather returning a boolean, status code, and some subset of information about the operation.


And how is that useful in the absence of pattern matching? If you write:

    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.


These libraries in languages like python remind me of when Windows 95 was new and a bunch of Win3.1 compatible clones of the win95 task bar appeared.

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.


Agreed. While algebraic data types are obviously a good idea, the best way to use Python is to accept it for what it is: a dynamically typed object-oriented language that's not very good at manipulating user-defined values (or even defining them to begin with), but has some pretty cool tricks of its own, like runtime metaprogramming with decorators and metaclasses.


I think you place python in a much smaller box that it deserves. before it was object-oriented it was functional and procedural. For certain things I use it in a purely functional way but have never felt the need for one of these libraries to do so.


Python has always had procedures, of course, but was it ever a functional language? Let's do a bit of fact-checkcing:

(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.


Words are defined by consensus and use. To a lot of people "functional" means having first-class functions (in particular: functions can be defined anywhere (i.e. the language has closures) and functions are first-class values (i.e. functions can be passed as function parameters and returned from functions; function values can be expressed separately from assignment (i.e. the language has lambdas))) and programming in a style that makes use of map, reduce, and filter; more generally programming with immutable values and functions that return transformations of those values (as distinct from programming with pervasive state that is mutated). This is by no means the only way to write Python, but it's practical in Python to a much greater extent than it is in, say, Java.


> (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.

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.


Sum types make very little sense in dynamically typed languages even with good manipulation of user-defined values. For instance Erlang does not have "formal" sum types, it wouldn't really bring anything useful to the table, the compiler does not typecheck by default (so the sum type would just be a complication with no benefits) and Dialyzer can simulate them by using type/value unions e.g.

    {ok, integer()} | {err, string()}


While I agree that there would be no point to user-defined sum types, constructor classes would be a strict improvement over what Erlang has.


I don't use this but you could argue that it's useful to have a standard encapsulation of failure/missing that isn't null? 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. So it's not completely useless whilst kind of crippled by no pattern matching?

Edit: although I guess that just using a tuple would be better for this maybe


> I don't use this but you could argue that it's useful to have a standard encapsulation of failure/missing that isn't null?

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.


It's useful if it's a monad (and functor) because then you have tools to avoid this check until the end of a series of possibly failing functions. It's only really useful with these tools though.

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.


> It's useful if it's a monad (and functor) because then you have tools to avoid this check until the end of a series of possibly failing functions.

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.


Which monad laws fail miserably in Haskell with non-termination, and for which monads?


All three monad laws and all monads. Just use `seq`.


Python has lambdas so you can still write:

    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.


> Python has lambdas so you can still write: (snippet)

Do you seriously consider this less of a hassle than branching on a frigging bit?


I think single entry/single exit is very valuable, and worth a bit of clunkiness for. And the branch version is also pretty clunky: either the long-winded "return ", abuse of "and"/"or", or the weirdly backwards expression version of "if" that Python has.


I don't disagree that branching on bits is clunky. That it's still less clunky than using Church or Scott-encodings speaks volumes of the practicality of the latter.


Also the link has a big article about why it's useful




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: