14 ms·
Left to Right Programming
- taeric 1y agoI'm curious on how much of this is basically overcome by the new tools? As much as vibe coding annoys me, I can't argue against how good autocomplete from these LLMs can be.
- nathan_compton 1y agoVery human of us to spend billions of dollars and tons of electricity to bang our programming languages into shape since sane syntax is slightly uncomfortable the first 10 minutes you work with it. I believe there are some strongly typed stack based languages where you really always do have something very close to a syntactically correct program as you type. But now that LLMs exist to paper over our awful intuitions, we're stuck with bad syntax like python forever.
- taeric 1y agoI'm honestly not sure how I feel on it, all told. On one level, I do prefer my code to be readable left to right and top to bottom. This, typically, means the big "narrative" functions up top and any supporting functions will come after they were used. Ideally, you could read those as "details" after you have understood the overall flow. On another level, though, it isn't like this is how most things are done. Yes, you want a general flow that makes sense in one direction through text. But this often has major compromises and is not the norm. Directly to programming, trying to make things context free is just not something that works in life. Directly to this discussion, I'm just not sure how much I care about small context-free parts of the code? Tangential to this discussion, I oddly hate comprehensions in python. I have yet to get where I can type those directly. And, though I'm asking if LLM tools are making this OBE, I don't use those myself. :(
- mrguyorama 1y agoSo far any "autocomplete" from an LLM has only served to insanely disrupt my screen with fourtey lines of irrelevant nonesense that cannot be turned off (the "don't suggest multiline autocompletes" option does not prevent LLM autocomplete from doing so) and has only served to be less useful than non-LLM based autocompletes were, which I was massively impressed with, because it was hyperlocal and didn't pretend that the niche code I'm writing is probably identical to whatever google or microsoft writes internally. I've had some minor success with claude, but enabling the AI plugin in intellij has literally made my experience worse, even without using any AI interactions.
- chrismorgan 1y ago> This is much more pleasent. Since the program is always in a somehwat valid state as you type it, your editor is able to guide you towards the Pit of Success. Although the subtitle was “programs should be valid as they are typed”, it’s weakened to “somewhat valid” at this point. And yes, it is valid enough that tooling can help, a lot of the time (but not all) at full capability. But there’s also interesting discussion to be had about environments where programs are valid as they are typed. Syntactically, especially, which requires (necessary but not sufficient) either eschewing delimition, or only inserting opening and closing delimiters together.
- scuff3d 1y agoI don't think making your tools happy is a particularly valid reason to code in one style vs another.
- xigoi 1y agoLeft-to-right is also much easier to wrap your head around. Just imagine the data going on a conveyor belt with a bunch of machines instead of having to wrangle a nested tree.
- scuff3d 1y agoA not insignificant number of functional languages disagree. You often read functional code from the inside out, which basically means you're reading right to left as you move up function calls. For the record though, "it's more readable" is a much better argument then "LSP mad"
- Philpax 1y agoRight-to-left is equivalent to left-to-right in this regard: you're still reading in one linear direction, as opposed to Python, where you have to read inside-out and revisit later parts of the line to correctly understand previous parts of the line.
- scuff3d 1y agoFair enough. And like I said, you're argument is certainly better than the article's. I'm not really a fan of list comprehensions, I usually just use for loops. It does seem consistent with Pythons syntax though. For loop is `for item in items` and comprehensions have 'item for item in items'.
- xigoi 1y agoNim has a macro in the standard library that allows you to use the actual for loop syntax as a comprehension. let primeSquares = collect: for n in 1..100: if n.isPrime(): n * n
- kmoser 1y ago> 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).
- nottorp 1y agoExactly. 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.
- smohare 1y ago[dead]
- AnimalMuppet 1y ago> You only write code sequentially when it's a new file. Often, not even then.
- f1shy 1y agoYes. Seems like an arbitrary limitation to force it.
- chrismorgan 1y agoThat’s not quite what the article’s about, though it’s interesting too.
- slowmovintarget 1y agoThis 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.
- ekusiadadus 1y ago> If you aren’t familiar with Rust syntax, |argument| result is an anonymous function equivalent to function myfunction(argument) { return result; }. > Here, your program is constructed left to right. The first time you type line is the declaration of the variable. As soon as you type line., your editor is able to suggest available methods. Yeah, having LSP autocomplete here does feel nice. But it also makes the code harder to scan than Python. Quick readability at a glance seems like the bigger win than just better autocomplete.
- chrismorgan 1y ago> But it also makes the code harder to scan than Python. Quick readability at a glance seems like the bigger win than just better autocomplete. It depends a lot on what you’re accustomed to. You get used to whichever style. Just like different languages use different sentence order: subject, object and verb appear in all possible orders in different languages, and their speakers get along just fine. There are some situations where one is clearly superior to the other, and vice versa. `text.lines().map(|line| line.split_whitespace())` can be read loosely as “take text; take its lines; map each line, split it on whitespace”. Straightforward and matching execution flow. `[line.split() for line in text.splitlines()]` doesn’t read so elegantly left-to-right, but so long as it’s small enough you spot the `for` token, realise you’re dealing with a list comprehension, and read it loosely from left to right as “we have a list made up of splitting each line, where lines come from text, split”. Execution-wise, you execute `text.splitlines()`, then `for line in`, then `line.split()`. It’s a bunch of left-to-rights embedded in a right-to-left. This has long been noted as a hazard of list comprehensions, especially the confusion you end up with with nested ones. Now you could quibble over my division of `for line in text.splitlines()` into two runs; but I think it’s fair. Consider how in Rust you get both `for line in text.split_lines() { … }` and `text.split_lines().for_each(|line| { … })`. Sometimes the for block reads better, sometimes .for_each() or .map() or whatever does. (But map(lambda …: …, …) never really does.) Python was my preferred language from 2009–2013 and I still use it not infrequently, but Rust has been my preferred language ever since. I can say: I find the Rust version significantly easier to read, in this particular case. I think the fact there are two levels of split contributes to this.
- hawk_ 1y agoI miss the F# pipe operator (https://learn.microsoft.com/en-us/dotnet/fsharp/language-reference/functions/#pipelines https://learn.microsoft.com/en-us/dotnet/fsharp/language-ref...) in other languages. It's so natural to think of function transform pipelines. In other languages you have to keep going to the left and prepend function names, and to the right to add additional args, parens etc ...
- jraph 1y agoYou'll be able to use it in PHP 8.5 :-) https://stitcher.io/blog/pipe-operator-in-php-85 https://stitcher.io/blog/pipe-operator-in-php-85
- lo_zamoyski 1y agon.b. pipe operators also exist in various forms in other languages. OCaml, Elixir, Clojure, Haskell...
- hawk_ 1y agoOh yes I didn't mean F# is the only one. Just that the other ones I am likely to work with i.e. C++, Java and Python don't have it.
- mrbananagrabber 1y agoI've had to migrate to mostly Python for my work, and this is the thing I miss the absolute most from R (and how it works so seamlessly with the tidyverse)
- deleted 1y ago[deleted]
- chrismorgan 1y agolen(list(filter(lambda line: all([abs(x) >= 1 and abs(x) <= 3 for x in line]) and (all([x > 0 for x in line]) or all([x < 0 for x in line])), diffs))) This really isn’t fair on Python. Python isn’t very much not designed for this style of functional programming. Plus you haven’t broken lines where you could. Rewrite it as a list comprehension and add line breaks, and turn the inner list comprehensions into generator expressions (`all([…])` → `all(…)`), and change `abs(x) >= 1 and abs(x) <= 3` to `1 <= abs(x) <= 3` (thanks, Jtsummers), and it’s much better, though it still has the jumping around noted, and I do prefer the functional programming approach. I’m just saying the presentation isn’t fair on Python. len([line for line in diffs if all(1 <= abs(x) <= 3 for x in line) and (all(x > 0 for x in line) or all(x < 0 for x in line))]) (Aside: change the first line to `sum(1 for line in diffs` and drop the final `]`, and it will probably perform better.) I also want to note, in the JS… Math.abs(x) instead of x.abs() (as seen in Rust). And, because nerd sniping, two Rust implementations, one a direct port of the JS: diffs.iter().filter(|line| { line.iter().all(|x| x.abs() >= 1 && x.abs() <= 3) && (line.iter().all(|x| x > 0) || line.iter().all(|x| x < 0)) }).count() (`x.abs() >= 1 && x.abs() <= 3` would be better as `(1..=3).contains(x.abs())` or `matches!(x.abs(), 1..=3)`.) And one optimised to only do a single pass: diffs.iter().filter(|line| { let mut iter = line.iter(); let range = match iter.next() { Some(-3..=-1) => -3..=-1, Some(1..=3) => 1..=3, Some(_) => return false, None => return true, }; iter.all(|x| range.contains(x)) }).count()
- Jtsummers 1y agoabs(x) >= 1 and abs(x) <= 3 Is also unidiomatic in Python. 1 <= abs(x) <= 3 Means the same thing and tightens it up a bit, and reads better since it's indicating that you're testing if something is in a range more clearly. EDIT: To add: The filter, list construction, and len aren't needed either. It's just: sum(map(predicate, diffs)) # this counts the number of elements in diffs which satisfy predicate, map is lazy so no big memory overhead Or alternatively: sum(predicate(diff) for diff in diffs) The predicate is complex enough and used twice, so it warrants extraction to its own named function (or lambda assigned to a variable), but even if it were still embedded this form would be slightly clearer (along with adding the line breaks and removing the extra list generations): sum(map(lambda line: all(1 <= abs(x) <= 3 for x in line) and (all(x > 0 for x in line) or all(x < 0 for x in line)), diffs))
- haunter 1y agoMildly related: Non-English-based programming languages. There are some right to left options like Arabic, Hebrew, and Persian https://en.wikipedia.org/wiki/Non-English-based_programming_languages#Based_on_non-English_languages https://en.wikipedia.org/wiki/Non-English-based_programming_...
- zzbzq 1y agoSQL 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.
- danielPort9 1y agoDon’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
- xigoi 1y agoIt’s a good language for beginners and, unfortunately, many people think that learning more languages is hard and useless.
- hnlmorg 1y agoThat’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 1y agoI'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.
- hnlmorg 1y agoJavaScript has been a backend language long before the web was the dominant platform. And one of the, admittedly many, reasons why web technologies like Electron and React Native exist is because it’s easier to find JavaScript developers vs Kotin, Qt or whatever. So youre not wrong but you’re also downplaying the network effect that led to the web becoming dominant. And a part of that was because developers didn’t want to learn something new.
- MonkeyClub 1y ago> JavaScript has been a backend language long before the web was the dominant platform. I don't think this holds. JavaScript was created as a frontend language specifically for web browsers. It wasn't until 2009 with the introduction of Node.js that JavaScript became a viable option for backend development. The web was already the dominant platform by then.
- xigoi 1y agoWhile methods partially solve this problem, they cannot be used if you are not the author of the type. Languages with uniform function call syntax like Nim or D do this better.
- cwilby 1y agoThis is it right here. I love the Python ecosystem but if I can find a JS alternative I'm using that for ergonomics.
- phkahler 1y agoDisagree. The first example the author seem to want something more like imperative programming, so the "loop" construct would come first. But then the assignment should come last. With the python syntax you get the thing you're assigning first - near the equals sign - and then where it is selected from and with any filtering criteria. It makes perfect sense. If you disagree that's fine, the whole post is an opinion piece.
- adwn 1y ago> the whole post is an opinion piece. It's not. The author gives objective reasons why Python's syntax is inferior – namely, that it makes IDE support in the form of discoverability and auto-completion more difficult.
- sgarland 1y agoThis presupposes that the reader likes or even uses auto-complete. I do not, and there are many others like me.
- adwn 1y agoAnd I'm sure there are people who program in Notepad or nano. If you want to develop software like it's the 80s again, go ahead, the rest of us appreciates at least basic IDE support.
- zahlman 1y agoI program in Vim, after having tried multiple IDEs and finding them all to be annoying. In the few places where they were helpful, they only highlighted what I disliked about either the language or a particular library.
- sgarland 1y agoNot liking auto-complete is a far cry from nano. I like syntax highlighting, I like on-the-fly type checking, I like linters, etc. I use Neovim and tmux because I value snappy performance, and never having to leave the keyboard. The reason I don’t like auto-complete is that it interrupts my thoughts. Once I’m typing code, I know what I want to do, and having things pop up is distracting. Before I’m typing, I’ll think about the problem, look at other code in the codebase, and/or read docs. None of that requires autocomplete, nor would it help me. If you like autocomplete, great, use it, but don’t assume that it’s a binary choice between plaintext editing and Copilot.
- pbiggar 1y agoIncidentally, Darklang had this built into the language/editor combo from the start. You can see it in the examples on our youtube, maybe this one: https://www.youtube.com/watch?v=NQpBG9WkGus https://www.youtube.com/watch?v=NQpBG9WkGus To make a long story short, we added features for "incomplete" programs in the language and tools, so that your program was always valid and could not be invalid. It was a reasonable concept, and I think could have been a game changer if AI didn't first change the game.
- deleted 1y ago[deleted]
- juancn 1y agoSQL 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...
- layer8 1y agoWhile I agree, some SQL editors do provide code completion for the SELECT clause if you type the FROM clause first.
- pjmlp 1y agoThat is my trick as well, but feels backwards, by now there could be a variant of SQL supporting FROM first.
- folkrav 1y agoI legitimately never even thought of this, and it feels very obvious in retrospect…
- pzmarzly 1y agoYou may find PRQL interesting. https://prql-lang.org/ https://prql-lang.org/
- dleeftink 1y agoPSQL (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 https://duckdb.org/community_extensions/extensions/psql.html [1]: https://duckdb.org/community_extensions/extensions/prql.html https://duckdb.org/community_extensions/extensions/prql.html
- 1y ago
- layer8 1y agoSome 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.
- waffletower 1y agoSo many engineers define their own narrow ergonomic values and then turn to the interwebs attempting to hammer their myopic belief into others as evangelical truth -- this reads as if the author believes there is a singular left-to-right process that a developer ought to adhere to. The author is oblivious to the non-linear compositional practices of other coders. It would be much more constructive to spend time creating specific tooling to aid your own process and then ask others if the values are also relevant to them. Not every developer embraces LSP, for example, as some are thwarted by its opinionated implementation. Not everyone is willing to give up local structure for auto-complete convenience.
- Zarathruster 1y agoOn 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.
- imtringued 1y agoI never understood this obsession with the keyword "from" Just do import SomeLibrary { asYetUnknownModule }
- throwitaway1123 1y agoAlternatively, 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/Reference/Statements/import#namespace_import https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
- Sammi 1y agoThis is a non starter for anything you want to publish online, as it breaks tree shaking which will cause size bloat and therefore slow loading.
- sebws 1y agoI don't think this is true. The example from the esbuild docs uses `import * as lib from './lib.js'` in an example for tree shaking. https://esbuild.github.io/api/#tree-shaking https://esbuild.github.io/api/#tree-shaking Although there are associated issues but they may be specific to esbuild. https://github.com/evanw/esbuild/issues/1420 https://github.com/evanw/esbuild/issues/1420
- throwitaway1123 1y agoYes, as you pointed out, not only can bundlers tree shake namespace imports, but they're literally used in the esbuild documentation to demonstrate the concept of tree shaking. The issue you linked to is referring to the case in which you import a namespace object and then re-export it. Bundlers like webpack and rollup (which vite uses in production) can tree shake this pattern as well, but esbuild struggles with it. If you're using esbuild instead of this: import * as someLibrary from "some-library" someLibrary.someFunction() export { someLibrary } You can still do this: import * as someLibrary from "some-library" someLibrary.someFunction() export * from "some-library" export { default as someLibraryDefault } from "some-library" Tree shaking works as expected for downstream packages using esbuild in the second case, which someone else in the linked issue pointed out: https://github.com/evanw/esbuild/issues/1420#issuecomment-961323828 https://github.com/evanw/esbuild/issues/1420#issuecomment-96...
- Terr_ 1y agoThat's fine for doing algebra in pure functions, but what about destructive commands or interactive scenarios? For example, using "rm" on the command line, or an SQL "delete". I would very much like those short programs to be invalid, until someone provides more detail about what should be destroyed in a way that is accident-resistant. If I had my 'druthers, the left-to-right prefix of "delete from table" would be invalid, and it would require "where true" as a safety mechanism.
- Twey 1y agoThe solution suggested by the author, I assume, is `table.where(true).delete()` and `all_my_data.rm()`, which indeed has the property you describe.
- deleted 1y ago[deleted]
- al2o3cr 1y ago[flagged]
- Twey 1y agoThis is the main reason I really like concatenative syntax for languages — this property is _enforced_ for programs (minus some delimited special cases, usually). It also neatly generalizes the special `self` argument so you can dispatch on the types of all the arguments.
- feelamee 1y ago> Suppose you have a FILE file and you want to get it’s contents. Ideally, you’d be able to type file. and see a list of every function that is primarily concerned with files. From there you could pick read and get on with your day. > Instead, you must know that functions releated to FILE tend to start with f, and when you type f the best your editor can do is show you all functions ever written that start with an f Why do you think that this is a problem of C? no one is stopping your tools from searching `fclose` by first parameter type when you wrote `file.`. Moreover, I know that CLion already do this.
- 0xfffafaCrash 1y agoI 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 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
- sgarland 1y agoIs the entire complaint that autocomplete doesn’t work well? Have you tried learning your language?
- Someone 1y agoIt’s more about learning the language’s library, which typically is too large to remember in detail, than about learning the language, which typically is small enough for that.
- sgarland 1y agoThe complaints listed seem to focus on attributes / methods of a class. You can read Python’s docs, and see the methods available to a str type, for example. To me, that’s learning the language. Learning its library would be more like knowing that the module `string` contains the function `capwords`, which can be used to - as the name suggests - capitalize (to title case) all words in a string. Hopefully, one would also know that the `str` class contains the method `upper`, so as not to confuse it with `string.capwords`.
- ebonnafoux 1y agoThe way people code with Python is by using its large ecosystem, few people only use the standart library. No one knows all the API, the more discoverability there is, the better.
- sgarland 1y agoI use stdlib whenever possible specifically because it’s huge, well-tested, and eliminates dependencies. I don’t know all of its API, but I do read the docs periodically (not all of them, but I try to re-read modules I use a lot, and at least one that I don’t).
- aquafox 1y agoThe 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.
- nxpnsv 1y agoI 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
- mb7733 1y agoI 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.
- nxpnsv 1y agoProbably not, I didn’t use R much during the last decade …
- itishappy 1y agoThe 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) ])
- rspencer 1y agoI had a similar thought a few years ago with an Advent of Code problem for which my solution in python might have been max(map(sum, input_list.split(None))) To decipher this the eye has to jump to the middle of the line, move rightwards, then to the left to see the "map" then move right again to see what we are mapping and then all the way to the beginning to find the "max". The author would probably suggest rust's syntax* of values.iter().split(None).map(Iterator::sum).max().unwrap_or(0) but I was learning q at the time so came up with the much clearer /s, right to left max((0^+)\)l *: Though neither python nor rust have such a nice `.split(None)` built in.
- papercrane 1y ago> Though neither python nor rust have such a nice `.split(None)` built in. Sorry, I'm not sure I understand what `.split(None)` would do? My initial instinct is that would would return each character. i.e. `.chars()` in Rust or `list(s)` in Python.
- rspencer 1y agoIt was intended to split a list of `int|None` into its non-none stretches. Much like how `string.split('x')` splits a string by matching the character 'x'
- papercrane 1y agoGotcha! In python there is a `split_at` function for this in the more-itertools package, but I don't think there is a concise way to do it in the stdlib.
- bear8642 1y ago>Sorry, I'm not sure I understand what `.split(None)` would do? Reading the docs [0] it seems `.split(None)` returns an array of the indivual characters without whitespace - so something like [c in list(s) if not whitespace(c)] [0] https://docs.python.org/3.3/library/stdtypes.html?highlight=split#str.split https://docs.python.org/3.3/library/stdtypes.html?highlight=...
- seniorsassycat 1y agoThis can obviously be expanded to top to bottom programming, and there's a related principle of reorder / relocation. If/else if fails the relocation principal across many languages, since the first must be if, and middles else if. Switch tends to pass. Languages that don't allow trailing commas also fail
- bogdanoff_2 1y agoOcaml has the pipe operator |> (kinda similar to pipe in Bash) for this purpose
- xg15 1y agoI think the most absurd syntax goes to Python again. Python offers an "extended" form of list comprehensions that lets you combine iteration over nested data structures. The irony is that the extensions have a left-to-right order again, but because you have to awkwardly combine them with the rest of the clause that is still right-to-left, those comprehensions become completely unreadable unless you know exactly how they work. E.g., consider a list of objects that themselves contain lists: toolboxes = [ Box(tools=["hammer"]), Box(tools=["wrench", "screwdriver"]) ] To get a list of lists of tools, you can use the normal comprehension: toolsets = [b.tools for b in toolboxes] But to get a single flattened list, you'd have to do: tools = [t for b in toolboxes for t in b.tools] Where the "t for b" looks utterly mystifying until you realize the "for" clauses are parsed left-to-right as [t (for b in toolboxes) (for t in b.tools)] while the "t" at the beginning is parsed right-to-left and is evaluated last.
- spapas82 1y agoThe proper way to parse or construct nested list comprehensions as explained in pep 202 is to think like you're using normal for: for a in b: for c in a: use(a,b,c) This would be [use(a,b,c) for a in b for c in a] Everything stays the same except the "use" part that goes in the front (the rule also includes the filters - if).
- lmm 1y ago> The proper way to parse or construct nested list comprehensions as explained in pep 202 is to think like you're using normal for: A syntax construct that requires you to think in a different syntax construct to understand it is not a good syntax construct. > Everything stays the same except the "use" part that goes in the front (the rule also includes the filters - if). Yeah, you consistently have to write the end first followed by the start and then the middle, it's just in most cases there's no middle so you naturally think you only have to flip the syntax rather than having to write it middle-ended like a US date.
- jdranczewski 1y agoI've taken to writing complex comprehensions like this over multiple lines (which I initially thought wasn't possible!). It's still a bit awkward, but the order of "fors" is the same as if one was writing nested for loops, which makes reasoning about it easier for me.
- nxobject 1y agoA 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 1y agoThis 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.
- 6yyyyyy 1y agoPlease no. I hate when the editor adds random shit I didn't type to the file.
- mdemare 1y agoOne of the things I dislike about function notation is that in f(g(h())) execution order is right-to-left. I like OO partly because execution order is writing order ( h().g().f() ) In Clojure I love the threading macro which accomplishes the same: (-> (h) (g) (f))
- dismalaf 1y agoIn Haskell there's also dot (edit - my bad, not dot, pipe) syntax which allows composing functions left to right. I believe Nim also has it. Just as a few more examples.
- _kb 1y agoI don’t think that’s correct. The dot operator is composition with the same semantics as when using normal math notation. (f . g) x is equivalent to f (g x)
- deleted 1y ago[deleted]
- projektfu 1y agof . g is still doing g first, then f, right? Haskell has pipelines in the Flow library. https://hackage-content.haskell.org/package/flow-2.0.0.9/docs/Flow.html https://hackage-content.haskell.org/package/flow-2.0.0.9/doc...
- dismalaf 1y agoOoops my bad. Been too long.
- valcron1000 1y agoNo need to import a third party library: you can use `Data.Function ((&))` for this: arg & f1 & f2 & f3
- paradox460 1y ago
- advael 1y agoThis is honestly why I love C++ ranges right now. The "pipe" syntax is a "left-to-right" of writing very powerful map/filter operations in contexts where I'd want a list comprehension but in a much more sensical (and for that matter customizable) order
- bvrmn 1y agoAuthor 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 1y agoI'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 1y agoWith 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 https://docs.python.org/3/library/itertools.html
- instig007 1y ago> 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 https://hoogletranslate.com/?q=map&type=by-algo
- bvrmn 1y agomaps and filters maybe. Other stuff is a wild west.
- frou_dh 1y agoIn 1990s-born scripting languages, it makes sense that there are plenty design choices that don't mesh well with static-analysis-driven autocompletion, because that was not at all part of the requirements for these languages at the time they were designed!
- kragen 1y agoIt was a pretty big deal by the time list comprehensions were added to Python, though, in 02000: https://peps.python.org/pep-0202/ https://peps.python.org/pep-0202/ It's true that Python didn't cater to static analysis at all.
- projektfu 1y agoSmalltalk is pretty strictly left-right but that becomes one of its faults as well, as infix operators are all at equal precedence.
- igouy 1y agoAnd not "valid as they are typed" in fact not validated until we explicitly ask.
- andsoitis 1y ago> Programs should be valid as they are typed. Not possible. There are more keystrokes that result in invalid programs (you are still writing the code!!) than keystrokes that result in a valid program. More seriously, I do think that one consideration is that code is read more often that written, so fluidity in reading and comprehension seem more important to me than “a program should be valid after each keystroke.
- ivanjermakov 1y agoTyping goes in the same directions as reading, so I expect many benefits to apply to both. But I agree that readability is much more important than writeability.
- michaelfeathers 1y agoThis is called point-free style in Haskell. Sometimes it is called a fluent-interface in other languages.
- bear8642 1y ago> Sometimes it is called a fluent-interface in other languages. Where've you heard it called that? I've normally heard tacit programming
- michaelfeathers 1y agoThe developers of JMock, the original library for Java.
- lock1 1y agoOr "point-less" style ;) Could you elaborate? AFAIK tacit programming tend to be scrambling around composition, paren, and args which makes left-to-right reading significantly harder for function with arity greater than 2. I find Java's method reference or Rust's namespace resolution + function as an argument much better than Haskell tacit-style for left-to-right reading.
- mrkeen 1y agoIt's chaining functions with a dot, just like you do in typical OO languages. When it's OO, it's a virtue that everyone loves - a "fluent interface". When it's FP - oh it's unreadable! Why don't they just break every line out with an intermediate variable so I know what's going on!
- montebicyclelo 1y ago> the Python code in the previous example is still readable Yes, I agree with the author, list comprehensions are readible, and I'd add, practical. > it gets worse as the complexity of the logic increases len(list(filter(lambda line: all([abs(x) >= 1 and abs(x) <= 3 for x in line]) and (all([x > 0 for x in line]) or all([x < 0 for x in line])), diffs))) Ok, well this is something that someone would be unlikely to write... unless they wanted to make a contrived example to prove a point. It would be written more like: result = sum(my_contrived_condition(x) for line in diffs) See also the Google Python style guide, which says not to do the kind of thing in the contrived example above: https://google.github.io/styleguide/pyguide.html https://google.github.io/styleguide/pyguide.html (Surely in any language it's possible to write very bad confusing code, using some feature of the language...) And note: x = [line.split() for line in text.splitlines()] ^- list comprehension is just a convenient shorthand for a `for loop`, i.e.: x = [] for line in text.splitlines(): x.append(line.split()) Just moving the `line.split()` to the front and removing the empty list creation and append.
- zahlman 1y agoWhy was this downvoted?
- deleted 1y ago[deleted]
- Naru41 1y agoI've often thought that if C just had a syntax like Go definding method, C would be much easier to write. uint16_t (MyStruct* s) some_func() { .. } uint16_t MyStruct_some_func(MyStruct* s) { .. } (I guess I should just use C++... but C++ is overwhelmed in many ways)
- pie_flavor 1y agoC# has something very similar to Python 'comprehensions', but written left to right like TFA describes: from line in text.Split('\n') select line.Split(null)
- kragen 1y agoBased 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.
- nromiun 1y ago> let words_on_lines = text.lines().map(|line| line.split_whitespace()); But you still have to define line without autocomplete.
- kristopolous 1y agocheck out gleam, it's the best there is here. Here's an example I found https://github.com/gleam-lang/example-todomvc/blob/main/src/todomvc/router.gleam https://github.com/gleam-lang/example-todomvc/blob/main/src/...
- AdieuToLogic 1y agoIt 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 https://leanpub.com/combinators/read#leanpub-auto-the-thrush
- corank 1y agoAs I understand it this is precisely why in Haskell and OCaml standard libraries we get lots of things like map : (a -> b) -> a list -> b list instead of map : a list -> (a -> b) -> b list The main argument (data to be operated on) is positioned last, after the others which are more like parameters that tune the function. It's to allow chaining these things up left to right like Unix pipes: map f l |> filter g |> ...
- AdieuToLogic 1y agoGreat point. The technique of currying method parameters such that the last one is the input to the operation also makes chaining Kleislis[0] quite nice, albeit using a bind operator (such as `>>=`) instead of the Thrush combinator operator (`|>`). 0 - https://bartoszmilewski.com/2014/12/23/kleisli-categories/ https://bartoszmilewski.com/2014/12/23/kleisli-categories/
- sriku 1y agoThis is almost FP vs OOP religious war in disguise. Similar to vim-vs-emacs ... where op comes first in vim but selection comes first in emacs. If you design something to "read like English", you'll likely get verb-first structure - as embodied in Lisp/Scheme. Other languages like German, Tamil use verbs at the end, which aligns well with OOP-like "noun first" syntax. (It is "water drink" word for word in Tamil but "drink water" in English.) So Forth reads better than Scheme if you tend to verbalize in Tamil. Perhaps why I feel comfy using vim than emacs. Neither is particularly better or worse than the other and tools can be built appropriately. More so with language models these days.
- dragonwriter 1y ago> If you design something to "read like English", you'll likely get verb-first structure If its imperative, sure. If its declarative and designed to read like English it will be subject first.
- deleted 1y ago[deleted]
- cesarb 1y ago> Other languages like German, Tamil use verbs at the end Doesn't German have the main verb on the second position? (For a simple example, "I drink water" would be "Ich trinke Wasser")
- andyferris 1y agoYeah I thought the German ambiguity was SVO vs OVS, “Fritz fished fish” vs “Fish, fished Fritz”..
- aredox 1y agoYeah, wrong one, but there are many languages out there where the verb is always at the end (Latin, Japanese I think).
- nyeah 1y agoInviting native German speakers to comment. Of course we anglophone-only HN geniuses can answer cesarb's question brilliantly on our own. But perhaps as a little sidebar we could also get your 'opinion'. Thanks! /s
- necovek 1y agoIn Rust, for..in loops have the same problem: you don't know the type and shape of the thing you are iterating on before you get to the "in" part. "Luckily", it does not allow you to call any methods on the object before you do. Similarly, Python list/dict/set comprehensions are a form of for-loop syntax sugar to easily create particular structure. One can use functools.maps to get exactly the same behavior of Rust example. If this was an all important text input microoptimization, we'd all be doing everything with pure functions like Lisp: yet somehow, functional languages are not the most popular even if they provide the highest syntax consistency.
- kragen 1y agoThat's an interesting idea: maybe instead of `for x, y in points: ...` (Python syntax) we should write `points do |x, y| ...` so the IDE has a maximum amount of type inference information available? That would also suggest writing variable declarations with the variable at the end, as in Forth or Levien's Io. 80 CONSTANT COLUMNS or `80 -> columns;`. It doesn't look anything like Lisp, though.
- chowells 1y agoAutocomplete-oriented programming optimizes for writing code. I don't think that's a good route to go down. Autocomplete is good for spewing out a large volume of code, but is that what we want to encourage? I'd much rather optimize for understanding code. Give me the freedom to order such that the most important ideas are up front, whatever the important details are. I'd much rather spend 3x the time writing code of it means I spend half the time understanding it every time I return to it in the future.
- necovek 1y agoAnother point to this is that we should switch to writing the numbers in the right order too: 123 should be three-hundred-twenty-one. This way, as soon as you type "1", it is truly one, you add a "2" and you know that's adding twenty... IIRC Arabic gets this right! /s
- teo_zero 1y agoHaha. Except in Arabic numbers are written as in English.
- necovek 1y agoRight, but all the other letters are written right-to-left too :)
- zahlman 1y agoYou joke, but you highlight an important problem with the argument. The idea is that we write `foo.bar`, where `foo` is in scope, exactly because `foo` is in scope. We don't write `bar from foo` or whatever, because it would be hard to reverse search the things that have `bar` in them. But which is more likely: will `foo` be Liskov-substitutable for `bar`, or will other things that contain a `bar` have a `bar` of a Liskov-substitutable type? Which is to say, the author is depending on a particularly idiosyncratic meaning of "valid". That said, the problem the author describes at the beginning can be easily worked around, if you like this kind of workflow: words_on_lines = [st # 'str' is a plausible autocompletion words_on_lines = [str.sp # must be either 'split' or 'splitlines', unless 'str' is shadowed words_on_lines = [str.split(line) for # 'line in' can be suggested words_on_lines = [str.split(line) for line in text.splitlines()] # IDE can automatically check type and refactor the idiom And it also isn't at all true that IDEs work left to right. They're constantly auto-typing the balancing close parentheses/braces/brackets for me and it's never clear to me what the intended flow is for moving past what was automatically typed, or whether manually typing that bracket explicitly (since I'm in my own "automatic typing") will double it up or not. There's nothing preventing the IDE from expecting you to type the clauses of the comprehension in a different order and I wouldn't at all be surprised to hear that someone has already implemented this.
- TZubiri 1y ago"This is bad because your editor can’t help you autocomplete it as you write it." No, you are bad because you use an editor with autocomplete. And it's not even debatable, it's like playing bowling with rails, or riding a bycicle with training wheels. Sure you can argue that you are more efficient at bowling and riding a bike with those, but you are going to be arguing alone, and it's much better to realize that python is one of the best languages at the moment and therefore one of the best languages ever, instead of being a nobody and complaining about a lanugage because you are too encumbered by your own ego to realize that you are not as good a programmer as you thought. Nothing wrong with being an amateur programmer or vibecoding or whatever, but if you come for the king you best not miss
- sureglymop 1y agoIt's nice if after typing `file.` you see what functions you can use. But what if there end up being too many options? What's next, a fuzzy finding search box for all possible functions? Contextually relevant ones based on the code you've already written?
- hnlmorg 1y ago> But what if there end up being too many options? That would suggest the file object needs to be refactored and split. > What's next, a fuzzy finding search box for all possible functions? Contextually relevant ones based on the code you've already written? IDEs already provide both those options.
- sureglymop 1y agoI understand, I think I moreso wanted to hint that there may still be more room for exploration when it comes to things like this. However, while I agree, I do want to challenge you to explain/show if and why this is the case (I know that you wrote "suggest"): > That would suggest the file object needs to be refactored and split.
- hnlmorg 1y agoI don’t want to overly generalise because there aren’t such things as absolutes with programming. But this was the thinking behind my comment: If you have dozens of methods then that might be an indicator that your object is too large in size and thus consumes too much memory even for simple tasks. It also might be a symptom that you’re trying to do too much with that object so perhaps some methods should be generalised, if possible. Or maybe the object is already too generalised and what you actually need is a more specific object that inherits some of the initial objects design? It might be that some of those methods should have been private, or even functions. Ultimately, it could be an indicator that the object is either badly designed or has evolved to the point that it’s now ready for a refactor. But it could also be the most optimal way to approach that problem too. In some situations. However it’s a good point to pause and reflect on whether the pain/reward threshold has been crossed.
- baalimago 1y agoYou don't need to be bothered with minute details such as syntax for counting string length anymore, you just need to know what you want to do. I mention this since OP is bringing up LSP's as an argument for why certain language's design is suboptimal. "Count length of string s" -> LLM -> correct syntax for string-count for any programming language. This is the perfect context-length for an LLM. But note that you don't "complete the line", you tell the LLM what you want to have done in full (very isolated) context, instead of having it guessing.
- hansvm 1y agoI don't think that meaningfully engages with the author's point. If you already know how to compute `len` on some arbitrary syntax soup then the difference is just a minor annoyance where you have to jump back in your editor, add a function call and some punctuation, and jump back to where you were to add some closing punctuation. It's so fast you'd never bother with an LLM, so despite real and meaningful differences existing the LLM discussion point isn't relevant. If you don't know how to compute `len` on some arbitrary syntax soup, I don't see how crafting an ideal prompt in a "full (very isolated) context" is ever faster than tab-completing things which look like "count" or "len."
- benrutter 1y ago> While the Python code in the previous example is still readable, it gets worse as the complexity of the logic increases. This bit is an aside in the article but I agree so much! List comprehensions in python are great for the simple and awful for the complex. I love map/reduce/filter because they can scale up in complexity without becoming an unreadable mess!
- userbinator 1y agoReading through this article only elicits a "WTF!?" from me. Your editor can’t help you out as you write it. You shouldn't need handholding when you're writing code. It seems like the whole premise of the author's argument is that you shouldn't learn anything about the language and programming should be reduced to choosing from an autocomplete menu and never thinking more than that. I've seen developers who (try to) work like this, and the quality of their work left much to be desired, to put it lightly. From there you can eventually find fread, but you have no confidence that it was the best choice. In C, you have to know ahead of time that fclose is a function that you’ll need to call once you’re done with the file. It's called knowledge. With that sort of attitude, you're practically begging for AI to replace you. No wonder people claim typing speed doesn't matter - they can barely think ahead one token, nevermind a statement or function, much less the whole design! Ideally your typing speed should become the bottleneck and you should be able to code "blind", without looking at the screen but merely outputting the code in your mind into the machine as fast as humanly possible. Instead we have barely-"developers" constantly chasing that next tiny dopamine hit of picking from an autocomplete menu. WTF!? When this descent into mediocrity gets applauded, it's no surprise that so much "modern" software is the way it is.
- teo_zero 1y agoAnd we don't need navigation systems on our cars because we should know where we're going, right? ;) Jokes apart, I think you're being too drastic. A good auto-complete is a nice feature, just like auto-indent, tab-complete, etc. Can it be abused? Sure. So what? Should we stop making it better for fear of abuse? The reference to AI is far-fetched, too. We're talking about tools to help you with the syntax, not the semantic. I may forget if the function is called read, fread, or file_read, but I know what its effect is. And finally, consider that if something is easier to parse for an editor, it most probably is for a human too. Not a rule, not working in 100% of cases, but usually exposing the user to the local context before the concept itself helps understanding.
- jaharios 1y ago> No wonder people claim typing speed doesn't matter - they can barely think ahead one token, nevermind a statement or function, much less the whole design! Ideally your typing speed should become the bottleneck and you should be able to code "blind", without looking at the screen but merely outputting the code in your mind into the machine as fast as humanly possible. Instead we have barely-"developers" constantly chasing that next tiny dopamine hit of picking from an autocomplete menu. WTF!? If writing code is an automated process for you, you are also begging for AI to be replace you. Just a more advanced one than the code-monkey in the OP.
- henrydark 1y agowords_on_lines = [ret for line in text.splitlines() for ret in [line.split()]]
- slybot 1y agoFunny how the article has no reference to C++ but still linking "Falling Into The Pit of Success". Did we all get our daily dose of subliminal messages from rust?
- mkl 1y agoI agree with the main ideas of this article. Context-first left-to-right seems like it would be easier for LLMs to write and autocomplete well too. This line, though, seems like it's using the wrong tools for the job: len(list(filter(lambda line: all([abs(x) >= 1 and abs(x) <= 3 for x in line]) and (all([x > 0 for x in line]) or all([x < 0 for x in line])), diffs))) To me it's crying out for the lines to be NumPy arrays: sum(1 for line in diffs if ((np.abs(line) >= 1) & (np.abs(line) <= 3)).all() and ((line > 0).all() or (line < 0).all())) There's no need to construct the list in memory if you're just counting, and dealing with whole lines at once is much nicer than going element by element. On top of that, this version is much more left-to-right.
- or_am_i 1y agoYeah, the `numpy` version still looks relatively cryptic (like, "line > 0" is still fine, but the numpy arrays broadcasting rules can quickly get out of hand) compared to the author's Javascript example, or any decent collections API in a typed "enterprise" language like C#/Java/Scala for that matter. Here's my personal favorite, a Kotlin version: diffs.countIf { line -> line.all { abs(it) in 1..3 } and ( line.all { it > 0} or line.all { it < 0} ) }
- podgorniy 1y agothe same is my beef with typescript's imports: import { MyClass } from './lib.ts' First you need to type what to import and then from where leaving. There is no linear way of discovering import options from the source of imports without extra jumping in the code. Alternatively linear completion, or (TIL https://en.wikipedia.org/wiki/Progressive_disclosure https://en.wikipedia.org/wiki/Progressive_disclosure) would be possible for imports of shape like: from './lib.ts' import { MyClass } If course authors of the import syntax had good reasons (which I don't know) to build stuff that way they've built it.
- thomasikzelf 1y agoReScript changed their api from data-last to data-first for this reason. Coupled with amazing type inference you will almost always receive correct autocompletions (that are type valid as well). It is a great experience. It is sometimes still a problem when you define a function without reference to it (because the types are unnkown ofcourse). You will have to add types to it or call the function before implementing it. There was also a great blogpost about it: https://www.javierchavarri.com/data-first-and-data-last-a-comparison/ https://www.javierchavarri.com/data-first-and-data-last-a-co...
- rf15 1y agoThis explains why I'm so confused and troubled by python's list comprehension syntax, thanks!
- saghm 1y agoThis is a view I've expressed for years now, and usually people are pretty repetitive to it until I follow it up with "and that's a big part of why I find Ruby easier for me to write scripts in than Python". As someone who has never really worked much on production code in either of those languages, I'll never understand how Python managed to end up in the situation where it's installed enough to be the scripting language of choice when people want to reach for something other than bash. Sure, Ruby has its warts, but it's not like people are really delving into the messy parts of Python either for their scripts, and at least Ruby hasn't had a debacle major version breaking changes in the last decade.
- pech0rin 1y agoI guess I’m so old that I remember time without autocomplete. Where programmers just knew what functions existed and how to use them. Usually by looking them up in a manual and then -gasp- remembering them. Languages shouldn’t be optimized for laziness or not using your brain. “Shut up old man” yes yes okay.
- aredox 1y agoThis is still bad ergonomics. Stair steps could be 450 mm high and work, but building codes make them 200 mm for a reason. And you are not "better" by saying that "I am fit enough to climb 450 mm steps, and you are all lazy for wanting stairs built to ergonomic standards".
- ksherlock 1y agoI don't know man. If a programmer can be replaced by a big old Tab key and a Homer Simpson keyboard bird, maybe LLM should take your job.
- melodyogonna 1y agoAnother thing to add to my list of superficial problems.
- marcelr 1y agothis is a bad take sometimes you want to think inside out its the difference between imperative thinking and declarative both are good, use each when applicable
- apps4datr2025 1y ago[dead]
- jonnycomputer 1y agolen(list(filter(lambda line: all([abs(x) >= 1 and abs(x) <= 3 for x in line]) and (all([x > 0 for x in line]) or all([x < 0 for x in line])), diffs))) Well, point taken about the ordering, but there are many more legible ways to have written that code. Not everything has to be a one liner. even: def f(diffs): cond = lambda line: all([1 <= abs(x) <= 3 for x in line]) and ( all([x > 0 for x in line]) or all([x < 0 for x in line]) ) items = filter(cond, diffs) return len(list(items))
- bkovitz 1y ago> Your editor can’t help you out as you write it. Must not be a vim user.
- mpweiher 1y agoThis was one of the changes I made to Smalltalk syntax for Objective-S[1]: The pipe "|" as an analog to the cascade ";", but sending to the result, like a normal pipe would. This avoids having to go back and add parentheses when you have a longer expression involving keyword sends. For example navigating a nested set of dictionaries self classDefs at:className. (self classDefs at:className) at:which. ((self classDefs at:className) at:which) at:methodName. vs. self classDefs at:className. self classDefs at:className | at:which. self classDefs at:className | at:which | at:methodName. [1] https://objecttive.st https://objecttive.st