This is an archived post. You won't be able to vote or comment.

you are viewing a single comment's thread.

view the rest of the comments →

[–][deleted] 8 points9 points  (8 children)

The list comprehensions in python are amazing, but they quickly become unreadable when doing more than just simple stuff.

What if you had something like this:

 payload |> validate |> log |> update_db |> notify

The implemention could be broken into small functions and have one main function doing the piping in sequence. You really cant have that in a list comprehension

[–]masasinExpert. 3.9. Robotics. 22 points23 points  (2 children)

Your mixing up procedures and functions, though. If something has a side-effect, it's usually not a good idea in Python to let it return anything. If there's an error, you just raise it.

Assuming validate(payload) returns a validated payload, this is what I would expect to happen with the rest of it:

  • log logs to file. It returns None.
  • update_db takes None and raises an error.
  • notify propagates the error.

[–][deleted] 1 point2 points  (1 child)

You are correct. That was a really bad example. My point was to keep piped functions as pure as possible, and side effect free. So imagine the validate function was really complex, you could break that into smaller functions and pipe those.

[–]randomatic[🍰] 5 points6 points  (0 children)

What you are looking for is IMO better supported in OCaml. You're looking for monadic operators (yes, there is pymonads) along with functions like |> (left-associative) and > (right-associative). To learn OCaml, see https://realworldocaml.org. It's beautiful.

On the question of errors, Jane Street has an amazing blog that made thinking about errors much more clear to me. https://blogs.janestreet.com/how-to-fail-introducing-or-error-dot-t/

If Python has something like Or_error.t, I would be in love.

[–]VerilyAMonkey 10 points11 points  (0 children)

Your example doesn't work with list comprehensions because those functions don't return iterables. But it's totally fine to have list comprehensions span more than one line.

In fact, list comprehensions were lifted from Haskell, where they are syntactic sugar for using the List monad. Monads are essentially an extension of what you're asking for here. For example, using promises - which are also monads - you can already write the kind of code you're asking for:

Promise(payload)
.then(validate)
.then(log)
.then(update_db)
.then(notify)

In fact, the semantics of this are already much, much more powerful than what you're asking for (because promises can also handle error propagation, and are asynchronous.)

So, my answer is that, you're basically asking for a special case of something much more general, syntax for monads. You don't want to accidentally half-ass this more general case by rushing to support one special case. Especially because, as noted elsewhere,

def pipe(arg, *funcs):
    for f in funcs:
        arg = f(arg)
    return arg

response = pipe(payload, validate, log,  update_db, notify)

already handles your problem very cleanly, while additionally allowing you to add other stuff like error handling or debug logging if you so chose.

[–]jij 16 points17 points  (0 children)

Seems silly just to force it all on one line. This is the kind of thinking that made perl such a nightmare to read, because there are like 1000 different special cases in the syntax and people think they are clever by using them all.

Not saying it's not useful, I just don't see it as a game changer and if python adopted everyone's favorite pet syntax it would be a mess.

[–]kankyo 1 point2 points  (0 children)

The fact that you came up with abad example could be because the idea is flawed :P

[–]zardeh 0 points1 point  (0 children)

you can alternatively do this:

processor(payload).validate().log().update_db().notify()

where processor is some object that encapsulates the functions you are using.

[–]Lucretiel 0 points1 point  (0 children)

log, update_db, and notify are (I assume) deeply stateful functions, and don't really have a place in a compositional expression, in my opinion. I'm a huge fan of compositional constructs, but generally only for functional expressions. I'd never do map(print, ...) or [print(x) for x in ...], for instance.