Skip to content(if available)orjump to list(if available)

Left to Right Programming

Left to Right Programming

192 comments

·August 18, 2025

juancn

SQL shows it's age by having exactly the same problem.

Queries should start by the `FROM` clause, that way which entities are involved can be quickly resolved and a smart editor can aid you in writing a sensible query faster.

The order should be FROM -> SELECT -> WHERE, since SELECT commonly gives names to columns, which WHERE will reference.

You could even avoid crap like `SELECT * FROM table`, and just write `FROM table` and have the select clause implied.

Never mind me, I'm just an old man with a grudge, I'll go back to my cave...

sgarland

It's written that way because it stems from relational algebra, in which the projection is typically (always?) written first.

>The order should be FROM -> SELECT -> WHERE, since SELECT commonly gives names to columns, which WHERE will reference.

Per the SQL standard, you can't use column aliases in WHERE clauses, because the selection (again, relational algebra) occurs before the projection.

> You could even avoid crap like `SELECT * FROM table`, and just write `FROM table` and have the select clause implied.

Tbf, in MySQL 8 you can use `TABLE <table>`, which is an alias for `SELECT * FROM <table>`.

balfirevic

> It's written that way because it stems from relational algebra, in which the projection is typically (always?) written first.

It's inspired by a mish-mash of both relational algebra and relational calculus, but the reason why SELECT comes first is because authors wanted it to read like English (it was originally called Structured English Query Language).

You can write the relational algebra operators in any order you want to get the result you want.

mamcx

> It's written that way because it stems from relational algebra,

A common misconception (that SQL is a realization of RA instead of barely based on it).

In RA, is in fact `Relation > Operator`

sgarland

Unless I am grossly misunderstanding your notation, I have always seen RA written with the operator first. Some examples:

https://cs186berkeley.net/notes/note6/

https://web.wlu.ca/science/physcomp/ikotsireas/CP465/W1-Intr...

https://home.adelphi.edu/~siegfried/cs443/443l9.pdf

kerkeslager

> It's written that way because it stems from relational algebra, in which the projection is typically (always?) written first.

Okay, so what?

We're not obligated to emulate the notational norms of our source material, and it is often bad to do so when context changes.

sgarland

I was explaining why it is the way that it is. If you'd like your own version of a parser, here's Postgres' [0]. Personally, I really like SQL's syntax and find that it makes sense when reading it.

[0]: https://github.com/postgres/postgres/tree/master/src/backend...

de6u99er

You can always start with the FROM clause and then add the SELECT clause above it if it's a out auto-,completion.

I usually start with: ``` select * from <table> as <alias> limit 5 ```

dleeftink

PSQL (and PRQL) use this ordering, and a similar pipe/arrow notation has recently been added to BigQuery.

Check out the DuckDB community extensions:

[0]: https://duckdb.org/community_extensions/extensions/psql.html

[1]: https://duckdb.org/community_extensions/extensions/prql.html

geysersam

In duckdb you can also start with `FROM .. SELECT ..` without using the PSQL extension.

But I haven't found a good editor plugin that is actually able to use that information to do completions :/ If anyone knows I'd be happy to hear it

paradox460

And in elixir land we've had similar pipes and composition through ecto, for the major rdbms

taeric

My main caveat here, is that often the person starting a select knows what they want to select before they know where to select it from. To that end, having autocomplete for the sources of columns is far far more useful than autocomplete for columns from a source.

I will also hazard a guess that the total number of columns most people would need autocomplete for are rather limited? Such that you can almost certainly just tab complete for all columns, if that is what you really want/need. The few of us that are working with large databases probably have a set of views that should encompass most of what we would reasonably be able to get from the database in a query.

jimbokun

I can’t think of an example where I knew the columns I wanted to select before I knew which table I wanted to select them from.

jayd16

Seems nonsensical. Column names have no meaning without the table. Table is the object, columns are the properties. What language goes Property.Object? All popular languages have this wrong?

kragen

Anytime you store a single property for all objects in an array or hash table, so, Fortran, BASIC, APL, Numpy, R, awk, and sometimes Perl. Parallel arrays are out of style but certainly not unheard of. They're the hot new thing in games programming, under the name "entity-component-system".

Even C when `property` is computed rather than materialized. And CLOS even uses that syntax for method invocation.

The only language I can think of that uses field(record) syntax for record field access is AT&T-syntax assembly.

taeric

I'm assuming you are largely just not thinking this one through? We are not modeling the domain, we are describing some data we want to select. Without knowing how it is modeled, I can give a brief "top line" for expected select statements on many data models. "I want average_weather, year, zip_code", "I want year, college_name, degree_name, median_graduate_salary, mean_graduate_salary", "..."

I don't think it is tough to describe many many reports of data that you would want in this way. Is it enough for you to flat out get the answer? No, of course not. But nor is it enough to just start all queries at what possible tables you could use as a starting point.

Izkata

Likewise when skimming code, having the attributes on the left and details on the right makes it much easier to see the data flow in the larger program - it's the same order as "var foo = bar()".

Zedseayou

Can you help me understand this situation? Almost always what I want to select is downstream of what I am selecting from, I can't write anything in SELECT until i understand the structure/columns available in FROM.

taeric

"I want 'first_name, last_name, and average(grade)' for my report." That is easy enough to state without knowing anything about how the data is normalized.

Back when I worked to support a data science team, I actually remember taking some of their queries and stripping everything but the select so that I could see what they were trying to do and I could add in the correct parts of the rest.

Arnavion

Yes, C#'s DSL that compiles to SQL (LINQ-to-SQL) does the same thing, `from` before the other clauses, for the same reason that it allows the IDE code completion to offer fields while typing the other clauses.

lackoftactics

This was a historical decision because SQL is a declarative language. I was confused for too long, I want to admit, about the SQL order: FROM/JOIN → WHERE → GROUP BY → HAVING → SELECT → ORDER BY → LIMIT

As a self-taught developer, I didn't know what I was missing, but now the mechanics seem clear, and if somebody really needs to handle SELECT with given names, then he should probably use CTE:

WITH src AS (SELECT * FROM sales), proj AS (SELECT customer_id, total_price AS total FROM src), filt AS (SELECT * FROM proj WHERE total > 100) SELECT * FROM filt;

balfirevic

> This was a historical decision because SQL is a declarative language

It would be equally declarative if FROM came first.

pjmlp

Kusto, the Azure query language for data analysis uses that form with piping as well.

https://learn.microsoft.com/en-us/kusto/query/?view=microsof...

Also the LINQ approach in .NET.

I do agree, that is about time that SQL could have a variant starting with FROM, and it shouldn't be that hard to support that, it feels like unwillingness to improve the experience.

0x00C0FFEE

I love working with KQL, it’s a very expressive query language.

layer8

While I agree, some SQL editors do provide code completion for the SELECT clause if you type the FROM clause first.

pjmlp

That is my trick as well, but feels backwards, by now there could be a variant of SQL supporting FROM first.

danielPort9

Don’t know why python gets so much love. It’s a painful language as soon as more than one person is involved. What the author describes is just the tip of the iceberg

williamscales

I find myself writing a very simple style of python that avoids list comprehensions and so on when working in a shared code base.

For a language where there is supposed to be only one way to do things, there are an awful lot of ways to do things.

Don’t get me wrong, writing a list comprehension can be very satisfying and golf-y But if there should be one way to do things, they do not belong.

ipython

I find list and dict comprehensions are a lot less error prone and more robust than the “manual” alternatives.

I would say unless you have a good reason to do so, features such as meta classes or monkey patching would be top of list to avoid in shared codebases.

dragonwriter

> For a language where there is supposed to be only one way to do things

That's not what the Zen says, it says that there should be one -- and preferably only one -- obvious way to do it.

That is, for any given task, it is most important that there is at least one obvious way, but also desirable that there should only be one obvious way, to do it. But there are necessarily going to be multiple ways to do most things, because if there was only one way, most of them for non-trivial tasks would be non-obvious.

The goal of Python was never to be the smallest Turing-complete language, and have no redundancy.

Philpax

If I have to write Python like Go, I'd rather just write Go. (Not disagreeing with you, but this is one of many reasons that Python is the least-favourite language that I use on a regular basis.)

SkepticalWhale

Maybe even painful for one dev once you need dependencies (virtualenv...)

Philpax

Thankfully, uv makes this a lot better, but it'll still be a while before that percolates to the entire ecosystem, if ever.

There are real concerns about tying the language's future to a VC-backed project, but at the same time, it's just such an improvement on the state of things that I find it hard not to use.

noosphr

Because everything that tries to fix it is just as painful in different ways.

I've had the displeasure of working in codebases using the style of programming op says is great. It's pretty neat. Until you get a chain 40 deep and you have to debug it. You either have to use language features, like show in pyspark, which don't scale when you need to trace a dozen transformations, or you get back to imperative style loops so you can log what's happening where.

mb7733

I used to agree with this completely, but type annotations & checking have made it much more reasonable. I still wouldn't choose it for a large project, but types have made it much, much easier to work with others' python code.

Python with strict type checking and its huge stdlib is my favourite scripting language now.

instig007

> Python with strict type checking and its huge stdlib is my favourite scripting language now.

It's time to try Scala 3 with Java libs' inbound interop: https://docs.scala-lang.org/scala3/book/scala-features.html

xigoi

It’s a good language for beginners and, unfortunately, many people think that learning more languages is hard and useless.

hnlmorg

That’s also precisely the reason why we have the clusterfuck that is the JavaScript ecosystem.

People complain that we can’t have nice things. But even when we do, enough developers will be lazy enough not to learn them anyway.

robmccoll

I'd argue it's because the web is the dominant platform for many applications and no other languages can offer a first class experience. if WASM had direct access to the DOM and web APIs and maybe a little more runtime support to lessen the bloat, I'd use something else.

shadowgovt

This hasn't been my experience, but we use the Google style guide, linters, and static type verification to cut down on the number of options for how to write the program. Python has definitely strayed from its "one right way to do a thing" roots, but in the set of languages I use regularly it gives about the same amount of issues as JavaScript (and far, far less than C++) regarding having to deal with quirks that vary from user to user.

ivanjermakov

I think because it's forgiving and the first language for many people, so they just stick to easy they know.

doug_durham

It's my 18th language and I prefer it over the alternatives. If I need to write an app I'll use Swift. If I need to do some web work I'll switch to TypeScript. To get work done it's Python all the way.

ivanjermakov

Agree. At some point grasping a new language takes hours and it makes sense to use it for tasks it excels at.

kmoser

> Programs should be valid as they are typed.

That would be nice if devs always wrote code sequentially, i.e. left to right, one character at a time, one line at a time. But the reality is that we often jump around, filling in some things while leaving other things unfinished until we get back to them. Sometimes I'll write code that operates on a variable, then a minute later go back and declare that variable (perhaps assigning it a test value).

dkarl

Code gets written once and read dozens or hundreds of times. Code that can be read sequentially is faster and easier to read than code that requires jumping around.

igouy

And @kmoser did not say code should not be read sequentially.

astrange

I agree with this, but it leads to another principle that too many languages violate - it shouldn't fail to compile just because you haven't finished writing it! It should fail in some other non-blocking way.

But some languages just won't let you do that, because they put in errors for missing returns or unused variables.

kmoser

How is it supposed to compile if you've written something syntactically invalid? You can make the argument that the compiler could interpret it in (perhaps even arbitrary) valid way that constitutes a valid syntax, but that's almost worse: rather than being chided with compiler warnings, you now end up with code that compiles but executes indeterminately.

astrange

Well I don't literally mean finished as in you haven't finished typing it. Although that's possible too - whitespace languages like Python tend to just work since they don't have end brackets, I suppose.

But you could have a compiled language where errors were limited to the function when possible, like by emitting asserts.

disconcision

there are approaches to ensure you always or almost always have syntactic validity, e.g. structured editing or error correcting parsing, though of course it's true that the more permissive the system is the more tortured some of the syntactic 'corrections' must be in extreme cases. the approach we're taking with http://hazel.org (which we approximate but don't fully succeed at yet) is: allow normal-ish typing, always correct the code enough to be ran/typechecked, but insert placeholders/decorations to telegraph the parse structure that we're using in case of errors or incompleteness.

Terr_

I feel that's a use case for compiler flags that convert warnings to errors or vice-versa, where the same source-code can either be "good enough to experiment with" or "not ready for release" depending on the context.

chrismorgan

That’s not quite what the article’s about, though it’s interesting too.

nottorp

Exactly. You only write code sequentially when it's a new file.

If i decide to add a new field to some class, i won't necessarily go to the class definition first, I'll probably write the code using that field because that's where the IDE was when i got the idea.

If I want to enhance some condition checking, i'll go through a phase where the piece of code isn't valid while I'm rearranging ifs and elses.

AnimalMuppet

> You only write code sequentially when it's a new file.

Often, not even then.

smohare

[dead]

PaulHoule

It's definitely a minor annoyance for me that IDEs assume that I type things in a different order than I really do.

layer8

Also, mutual recursion. ;)

f1shy

Yes. Seems like an arbitrary limitation to force it.

slowmovintarget

This is a "let 'im cook" instance.

Reading through the article, the author makes the argument for the philosophy of progressive disclosure. The last paragraph brings it together and it's a reasonable take:

> When you’ve typed text, the program is valid. When you’ve typed text.split(" "), the program is valid. When you’ve typed text.split(" ").map(word => word.length), the program is valid. Since the program is valid as you build it up, your editor is able to help you out. If you had a REPL, you could even see the result as you type your program out.

In the age of CoPilot and agent coders I'm not so sure how important the ergonomics still are, though I dare say coding an LSP would certainly make one happy with the argument.

layer8

Some IDEs provide code templates, where you type some abbreviation that expands into a corresponding code construct with placeholders, followed by having you fill out the placeholders (jumping from one to the next with Tab). The important part here is that the placeholders’ tab order doesn’t need to be from left to right, so in TFA’s example you could have an order like

    {3} for {2} in {1}
which would give you code completion for {3} based on the {1} and {2} that would be filled in first.

There is generally a trade-off between syntax that is nice to read vs. nice to type, and I’m a fan of having nice-to-read syntax out of the box (i.e. not requiring tool support) at the cost of having to use tooling to also make it nice to type.

This is not meant as an argument for the above for-in syntax, but as an argument that left-to-right typing isn’t a strict necessity.

aquafox

The consensus here seems to be that Python is missing a pipe operator. That was one of the things I quickly learned to appreciate when transitioning from Mathematica to R. It makes writing data science code, where the data are transformed by a series of different steps, so much more readable and intuitive.

I know that Python is used for many more things than just data science, so I'd love to hear if in these other contexts, a pipe would also make sense. Just trying to understand why the pipe hasn't made it into Python already.

atq2119

The next step after pipe operators would be reverse assignment statements to capture the results.

I find myself increasingly frustrated at seeing code like 'let foo = many lines of code'. Let me write something like 'many lines of code =: foo'.

levocardia

The pipe operator in R (really, tidyverse R, which might as well be its own language) is one of its "killer apps" for me. Working with data is so, so pleasant and easy. I remember a textbook that showed two ways of "coding" a cookie recipe:

bake(divide(add(knead(mix(flour, water, sugar, butter)),eggs),12),450,12)

versus

mix(flour, water, sugar, butter) %>% knead() %>% add(eggs) %>% divide(12) %>% bake(temp=450, minutes=12)

So much easier!

nxpnsv

I don't know if

   result = (df
      .pipe(fun1, arg1=1)
      .pipe(fun2, arg2=2)
   )
is much less readable than

   result <- df |> 
      fun1(., arg1=1) |> 
      fun2(., arg2=2)
but I guess the R thing also works beyond dataframes which is pretty cool

itishappy

The pipe operator uses what comes before as the first argument of the function. This means in R it would be:

    result <- df
      |> fun1(arg1=1)
      |> fun2(arg2=2)
Python doesn't have a pipe operator, but if it did it would have similar syntax:

    result = df
      |> fun1(arg1=1)
      |> fun2(arg2=2)
In existing Python, this might look something like:

    result = pipe(df, [
      (fun1, 1),
      (fun2, 2)
    ])
(Implementing `pipe` would be fun, but I'll leave it as an exercise for the reader.)

Edit: Realized my last example won't work with named arguments like you've given. You'd need a function for that, which start looking awful similar to what you've written:

    result = pipe(df, [
      step(fun1, arg1=1),
      step(fun2, arg2=2)
    ])

superbatfish

>Implementing `pipe` would be fun, but I'll leave it as an exercise for the reader.

I like exercise:

https://gist.github.com/stuarteberg/6bcbe3feb7fba4dc2574a989...

Izkata

Python supports a syntax like your first example by implementing the appropriate magic method for the desired operator and starting the chain with that special object. For example, using just a single pipe: https://flexiple.com/python/python-pipeline-operator

The functions with extra arguments could be curried, or done ad-hoc like lambda v: fun1(v, arg1=1)

mb7733

I haven't used R in forever, but is your `.` placeholder actually necessary? From my recollection of pipe operator the value being pipe piped is automatically as the first argument to the next function. That may have been a different implementation of a pipe operator though.

AdieuToLogic

It seems to me what the author desires is linguistic support for the Thrush combinator[0]. Another colloquial name for it is "the pipe operator."

Essentially, what this combinator does is allow expressing a nested invocation such as:

  f(g(h(x)))
To be instead:

  h(x) |> g |> f
For languages which support defining infix operators.

EDIT:

For languages which do not support defining infix operators, there is often a functor method named `andThen` which serves the same purpose. For example:

  h(x).andThen(g).andThen(f)
0 - https://leanpub.com/combinators/read#leanpub-auto-the-thrush

Zarathruster

On the other hand, Python does have "from some_library import child_module" which is always nice. In JS we get "import { asYetUnknownModule } from SomeLibrary" which is considerably less helpful.

throwitaway1123

Alternatively, with namespace imports in JS you can write [1]:

  import * as someLibrary from "some-library"
  someLibrary.someFunction()
Which works pretty well with IDE autocomplete in my experience.

[1] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...

imtringued

I never understood this obsession with the keyword "from"

Just do

    import SomeLibrary {
        asYetUnknownModule
    }

kragen

Based on Haskell do-notation, Firefox's unfortunately withdrawn array comprehensions, and C# LINQ, I've been thinking about this:

    [for b in c
     let d = f(b) 
     for e in g if h else m 
     where p(d, e): b, d, e]
as alternative syntax to Python's

    ((b, d, e)
     for b in c
     for d in [f(b)]
     for e in (g if h else m)
     if p(d, e))
This solves five problems in Python listcomp syntax:

1. The one this article is about, which is also a problem in SQL, as juancn points out.

2. The discontinuous scope problem: in Python

    [Ξ for x in Γ for y in Λ]
x is in scope in Ξ and Λ but obviously not Γ. This is confusing and inconsistent.

3. The ambiguity between conditional-expression ifs and listcomp-filtering trailing ifs, which Python solves by outlawing the former (unless you add extra parens). This is confusing when you get a syntax error on the else, but there is no non-confusing solution except using non-conflicting syntax.

4. let. In Python you can write `for d in [f(b)]` but this is inefficient and borders on obfuscated code.

5. Tuple parenthesization. If the elements generated by your iteration are tuples, as they very often are, Python needs parentheses: [(i, c) for i, c in enumerate(s) if c in s]. That's because `[i, c` looks like the beginning of a list whose first two items are i and c. Again, you could resolve these conflicting partial parses in different ways, but all of them are confusing.

zzbzq

SQL has this problem since it wants the SELECT list before the FROM/JOIN stuff.

I've seen some SQL-derived things that let you switch it. They should all let you switch it.

bvrmn

Author picked up a quite convenient example to show methods/lambda superiority.

I prefer list/set/dict comprehensions any day. It's more general, doesn't require to know a myriad of different methods (which could not exists for all collections, PHP and JS are especially bad with this) and easily extendable to nested loops.

Yes it could be `[for line in text.splitlines() if line: for word in line.split(): word.upper()]`. But it is what it is. BTW I bet rust variant would be quite elaborate.

lazarovitch

I'm a big fan of Python syntax, but really comprehensions don't make any sense to me efficiency wise (even for readability). Python syntax would become perfect with filter() and map() :')

sn9

With judicious use of generators, and with itertools [0] when needed, they can be quite efficient for not much effort.

And these are flexible tools that you can take with you across projects.

[0] https://docs.python.org/3/library/itertools.html

instig007

> I prefer list/set/dict comprehensions any day. It's more general, doesn't require to know a myriad of different methods

It's the opposite, your knowledge of the standard set of folding algorithms (maps, filters, folds, traversals) is transferable almost verbatim across a wide range of languages: https://hoogletranslate.com/?q=map&type=by-algo

nxobject

A minor corollary to this is that, as the user types, IDEs should predictably try to make programs valid – e.g. via structured editing, balancing parens in Lisp like paredit, etc.

ivanjermakov

This moves language design responsibility to the tool from the language itself. It might be okay to not hurt language elegance (e.g. lisp syntax), but in general I expect language to be convenient regardless of dev environment.

0xfffafaCrash

I really want JS/TS to adopt the pipeline operator which has been in a coma in stage 2 after eternal bikeshedding at TC-39

https://github.com/tc39/proposal-pipeline-operator

It would make it possible to have far more code written in the way you’d want to write it