1. 3

    A while back I moved from z to fasd. That plus fzf makes for some nice navigation and completion.

    1. 1

      Any reason why you moved? I’m looking for a pure POSIX alternative to z (supports only zsh and bash AFAIK), and fasd seems to be it.

    1. 3

      else does not run on loop termination. It only runs if the loop condition was never true in the first place, ergo if there is no loop execution at all.

      Also, I believe that fundamental syntax tutorials do not belong on Lobsters. Short-form content like this would be better-suited to, e.g., a Twitter thread. But I’m willing to hear counterpoints.

      1. 10

        I don’t think I agree with syntax tutorials not belonging on Lobsters. I certainly wouldn’t want them to be the majority of the content, but a) this is a feature pretty unique to python that isn’t super well known and b) most new programming language projects include a nonzero amount of fundamental syntax tutorials.

        1. 2

          b) most new programming language projects include a nonzero amount of fundamental syntax tutorials.

          And that’s exactly it: in my mind, Lobsters is for new or novel things. If a story is old, ideally it’s something that’s being uncovered and that most people haven’t seen. Maybe this story occupies a nice little niche of things most people have seen, yet many don’t fully understand.

          I mainly recall the barrage of Ruby tutorials that came a while back, muddling the front page and turning me off to these sorts of short one-off articles. But I checked the author’s archive and it seems they’ve got plenty of interesting stuff under their belt. I really look forward to seeing more of that sort!

        2. 7

          else does not run on loop termination. It only runs if the loop condition was never true in the first place, ergo if there is no loop execution at all.

          That’s not correct. If numbers is [0,2,4,6,8] it will print “No odd found”. The else always runs if break is not hit, regardless of what’s in the iterable being iterated.

          1. 6

            Unless I misunderstand you, it looks like what you’re saying is exactly wrong. For example:

            ❯ cat /tmp/test.py && python3 /tmp/test.py
            def print_first_odd(numbers):
                for x in numbers:
                    if x % 2 != 0:
                        print(f'Found odd number: {x}')
                        break
                else:
                    print(f'No odd numbers in [{",".join(map(str, numbers))}]')
            
            
            print('---------------------')
            print_first_odd([1,2,3,4,5])
            print_first_odd([2,4,6,8,10])
            print_first_odd([])
            print_first_odd(None)
            
            ---------------------
            Found odd number: 1
            No odd numbers in [2,4,6,8,10]
            No odd numbers in []
            Traceback (most recent call last):
              File "/tmp/test.py", line 14, in <module>
                print_first_odd(None)
              File "/tmp/test.py", line 2, in print_first_odd
                for x in numbers:
            TypeError: 'NoneType' object is not iterable
            

            I’ve been using python for non-trivial things since 2001 and I didn’t know else worked this way in python until today. I was happy to see this linked on lobste.rs and would have found it harder to read on twitter than on this easy-for-me-to-read blog post.

            And I would also posit that the fact that you didn’t understand the syntax and I didn’t know about it after using python (not every day but plenty) for this long would make this something other than a “fundamental syntax tutorial”.

            1. 5

              Looks like I can’t edit the original comment for corrections, but I now understand the distinction with respect to for/else. Thanks for the insightful replies, I guess maybe I learned something from this story after all!

              Also, maybe the number of people agreeing with my incorrect statement indicates how poorly understood this feature is. Perhaps a reason to use it sparingly…

              1. 3

                Your incorrect statement forced me to try the feature out and understand it. (At least in the context of for…else) So thanks for posting it.

                After letting the construct bounce around my brain for a few hours and looking at a couple of places I could have used it, I have two more thoughts:

                1. I kind of like the idea, especially when it comes to eliminating sentinel values. I don’t think this is just an affinity for code golf, either. Sentinels add their own form of ugly noise, and I’ve certainly spent time in the past chasing bugs that this construct would’ve very likely prevented me from writing.

                2. I really hate that they reused else for it. Coming up with a different name, or even reusing break with a colon after it would’ve been better IMO. I think that’s mostly because

                for foo in bar:
                    if baz:
                        do_bip()
                

                is such a very common construct. My eyes have a hard time telling whether an else is indented to match the if or to match the for and the difference between the two is quite load bearing here. In a curly brace language, it might be less challenging to read.

                So I’m finding myself inclined to use it very sparingly after thinking about it. I suspect there are several places where killing sentinel values will be worth it as long as it’s accompanied by a clear comment, though, even if the readability factor prevents me from using it anywhere else.

                1. 2

                  Personally, I guess the rationale comes from the try: construct and not the if: meaning of the word. The latter is of course the one that most people think of first, which also happened for me.

                  From the meaning of the keyword I would have preferred finally, but that would clash with how it is interpreted in a try block.

                  1. 1

                    Personally, I guess the rationale comes from the try: construct and not the if: meaning of the word. The latter is of course the one that most people think of first, which also happened for me.

                    The rationale for for/else is that for is just sugar for while. The rationale for while/else is that while is just sugar for if condition: jump backwards and then the else is just attached to that if.

                  2. 1

                    I suppose other languages tend to replace “else” with “finally” in some of these. I agree that for…else sounds more like “either loop or do this”, rather than “loop and then do this”.

                    On the other hand, while good keywords are, well good, more reserved (common) words are bad. I’m not sure if I would really prefer, say for..then or try..then.

                    1. 1

                      I appreciate what you said about sentinels. I think an aspect of what’s gross about them is the fact that they’re a state-based solution. Granted, I think any solution would require state in the background, but I think a better semantic solution is in store—and overloading standard for is not it. My best bet is filtering on a condition, then matching the resulting sequence to see whether it’s empty:

                      def find_odd(numbers):
                          odds = filter(lambda n: n % 2 == 1, numbers)
                          try:
                              odd = next(odds)
                              print("Found", odd)
                          except StopIteration:
                              print("No odd found")
                      

                      That way, all the “iteration” (lazy, in this case!) happens before the branching, and the branching itself boils down to a basic if/else construct. I just (ab)used exceptions here because they happened to be a more direct, if perhaps slower, expression of intention.

                  3. 2

                    I suppose you’re referring to while/else? The for/else example given could be a little more clear if it didn’t contain an if. As is, every x in numbers is indeed tested. If you omit the break, then the else branch is executed at loop end.

                    1. 2

                      I had a colleague who used this, else on for loops to determine if the collection was empty - which looked weird, and took a while for me to decipher.

                      I don’t see why it’s more clear to put an else clause on a loop, instead of testing if the collection is (not) empty before using it.

                      1. 4

                        In Python, it is idiomatic to “shoot first and ask questions later.” For example, if you want to read a file, you wouldn’t (or shouldn’t) waste time and code checking to see if the file exists, whether it has the right permissions, is not empty, and so on before trying to read from it. You just try to read from it and then if it can’t be read for whatever reason an exception will be raised and your application decides what (if anything) to do about at that point. This results in code that is easier to read and needs far less boilerplate for common trivial things.

                        If your colleague was only using the loop to determine if the collection was empty, instead of if not len(collection), yes, that would be very weird. But if it was also used to do some work in the non-empty case, that’s Pythonic.

                        1. 1

                          So something like this

                          for handset in handsets:
                              <do something with item>
                          else
                              raise <custom exception that indicates test failure with extra info>
                          

                          Is pythonic or have I misunderstood what you meant, bityard?

                          1. 2

                            Actually I went back and read the docs. My point about the Python idiom still stands but we were both wrong about the role of an else clause attached to a for loop. The else clause is executed after the whole list (or whatever) has been iterated over and no break statement was encountered. So you can’t use an else to determine whether the list is empty.

                            You could check to see whether handset was defined in the else clause to determine if the list was empty (handset will be undefined if handsets is empty and the loop never iterated) but that would be a very roundabout way to avoid len('handsets').

                            1. 1

                              Alright, it does make a bit more sense now :-)

                              But it also confirms my feeling of it being a bit dodgy instead of the len(handsets)

                    1. 4

                      I haven’t taken more than a brief glance at the source code, so if anyone is more familiar with this project, does it use cubic chunks (instead of “pillars” like Minecraft)? I’ve been interested in exploring procedural generation in voxel worlds like these and the issue with non-cubic chunks is that blocks that are far above or below the camera (completely out of sight) must be stored in memory, making worlds of extreme height really difficult to generate.

                      It also appears that worlds are pre-generated, meaning that they are of a fixed size, yes? I wonder how hard it would be to convert this to generating each chunk as it is loaded.

                        1. 1

                          Oh. Yes, it does. Thank you!

                      1. 13

                        This appeals to me. However:

                        But how can I then keep the style and layout of all my posts and pages in sync? Simple: don’t! It’s more fun that way. Look at this website: if you read any previous blog post, you’ll notice that they have a different stylesheet. This is because they were written at different times. As such, they’re like time capsules.

                        While that’s kind of cool in its own way, I don’t prefer it. Especially when it comes to a site menu.

                        My first web sites were hand-coded HTML. My motivation to learn PHP was that I wanted a consistent menu across all pages, and copy-paste was not maintainable, so I landed on PHP’s include. From there it was down the rabbit hole to becoming a web developer.

                        I use a static site generator now for nathanmlong.com, which I mostly write in Markdown. It wouldn’t kill me to write HTML, but I don’t want to copy and paste a menu everywhere.

                        1. 8

                          Case in point about the downsides, the cv link is correct on the author’s homepage. It is not correct on this page. That’s an easy mistake to make, and I’ve definitely made versions of it. However, it’s much more pleasant to fix when you can fix it everywhere by updating a template.

                          1. 3

                            Thanks for the heads up :-)

                            Edit: Solved by sed -i 's,href="cv",href="../cv",' */*.html. In my mind, simpler than a CMS or static site generator.

                            1. 3

                              “Simpler”, sure, maybe. At least for now. But maybe it won’t always be such a trivial sed command. Maybe you wrote the html slightly different in certain spots.

                              A simple or custom-built static site generator would avoid mistakes like this altogether. You could have one file for your head element. Nicer menus, sidebar, etc. And you could still write most or all of it in pure html if you wanted to.

                              Simpler doesn’t necessarily mean better.

                              1. 1

                                If you need the same template for all of your pages, then yes – a templating engine is a good idea.

                                But if you don’t need this, then a templating system makes the process unnecessarily complicated. Creating a template in a special language and fitting all pages to the same mold takes much more effort than most realize, especially in comparison with just writing single HTML pages.

                                For example, look at my software page. I have some fancy HTML and CSS to render sidenotes in the margin (unless you use a small screen). Because the page is “self-contained”, I don’t have to worry if I ever edit the style sheet for other posts. But if I used a templating engine, I would have to worry about it.

                          2. 5

                            Everything old is new again (or something like that)… you can always use server side includes for common elements.

                            1. 2

                              I like keeping my content and the final HTML site separate, and using the content to generate the site. It makes my content more flexible, but also makes generating the global menus easy, which is important to me so that my readers get a good experience.

                              1. 2

                                I haven’t actually used it but the caddy web server appears to have built-in templating features: https://caddyserver.com/docs/template-actions

                                1. 1

                                  Dreamweaver supported keeping sites’ themes consistent when I tried it long ago. It was templates or something. Maybe one of the open editors can do that, too. Otherwise, it would be a good feature for them to add.

                                  1. 1

                                    I hear you. I think the obvious solution then is to use something like PHP or SSI. Of course, that’s another layer of complexity, but not as much as a static site generator or CSS.

                                  1. 6

                                    I think this article is pretty deceiving. You would never tune piano like this. At the bottom, it states:

                                    This tuning is called twelve-tone equal temperament. It’s “the most common tuning system since the 18th century”

                                    However, pianos are not tuned using twelve-tone equal temperament. They’re stretch tuned. Some electric pianos are also stretch tuned, and any piano sample plugin will also be stretched tuned. And indeed, the Wikipedia article Piano Key Frequencies that’s linked at the bottom says:

                                    This list of frequencies is for a theoretically ideal piano. On an actual piano the ratio between semitones is slightly larger, especially at the high and low ends, where string stiffness causes inharmonicity, i.e., the tendency for the harmonic makeup of each note to run sharp. To compensate for this, octaves are tuned slightly wide, stretched according to the inharmonic characteristics of each instrument. This deviation from equal temperament is called the Railsback curve.

                                    (I also take issue with the phrasing “theoretically ideal piano”. An “ideal” piano that’s used for actual music will be stretch tuned.)

                                    Further reading: http://piano.detwiler.us/

                                    edit: some comments on Hacker News also discuss this: https://news.ycombinator.com/item?id=19305258

                                    1. 10

                                      I really loved the spirit of this article, particularly this bit from the conclusion:

                                      No matter how much data you have you still have to ask the right questions. It’s painfully easy to have good intentions but ask the wrong question and find the wrong answer.

                                      And I especially loved the example given in the bonus section. I don’t think I would’ve ever thought that geography (or rather, download speed) was the cause of the increase in average page load time. While it’s such a simple answer, thinking about how to arrive there (or: find the right question) just feels so far out there to me. It reminds me of the Ultimate Answer/Question from The Hitchhiker’s Guide to the Galaxy. Deep Thought is able to arrive at the Answer (“42”), yet a much more powerful computer must be designed and run for even longer in order to find the Ultimate Question.