How? I tried recreating the scenario from the article (the section "First rebase –onto") and ran the first rebase with "--update-refs":
$ git checkout feature-1
$ git rebase --update-refs main
Successfully rebased and updated refs/heads/feature-1.
Updated the following refs with --update-refs:
refs/heads/feature-2-base
But all it did was update feature-2-base. It still left feature-2 pointing to the old commits. So I guess it automates "git branch -f feature-2-base feature-1" (step 3), but it doesn't seem to automate "git rebase --onto feature-1 feature-2-base feature-2" (step 2).
Yeah, you need to rebase the tip of the feature branch stack. git will then update all the refs that point to ancestor commits that are moved. So in this case
If I may, the evil is not in the top level await but in the use of a top level await in a module that exports. That is evil. But a top level await in a programs main script seems ok to me.
LOLz! As someone who swaps caps with control on all machines as well as buying keyboards which lack caps for a control key, this site is truly horrifying!
In struggled with this trend even before the AI hype. I love learning and focused my work towards always learning. But it takes energy and lots of work; some days are better than others. My love of learning keeps me motivated over time. However, I’ve noticed a downward trend of juniors who are focused on being spoon fed the answers and avoiding any learning. It drives me nuts and I don’t know how to reconcile this kind of mental model.
The anti-make-reinvent-everything mentality has bugged me for decades. I never understood why every language seems compelled to reinvent the build system. Especially when the output is file based.
I’m going to use Cargo as a random example here. With Cargo, I can:
* Download a bunch of dependencies
* Build them with their own specific instructions
* Build all the code in my own arbitrarily complex source tree
* Link it all into a production-quality executable
…all without having to know or care how to do all those things myself.
I know how to do all those things. It no longer sparks joy in me to repeat the boilerplate of doing all them again and again in every project I start. It’s hugely appealing to me for a language to handle all that piffle just as it handles memory management, type enforcement, encoding loops as gotos, and all the rest.
I can write assembler, or machine code if needed. I can also find outdated .o files and rebuild them. But why? Let someone else handle the details so I can write code.
I use Makefile for my static blog that is powered by a static site generator. Something I love about this is I can have one place to describe all the different build tools. I can have PlantUML to generate PNG diagrams, Vim to generate syntax highlighted code snippets, openssl to generate certs, etc. I was one of the best choices I made for that project.
Make is great when working with the languages it was originally designed and intended for (C, C++, yacc, bison, etc). Basically the GNU ecosystem.
It's far less great when working with anything else, like Go, Python, JS/TS, etc.
People want great tools that work well with the way their software and teams work. Seems logical to me.
After all, someone reinvented the build system when they created make to solve the problems of their time. What if they just stuck with csh or plain POSIX compatible build scripts?
I think it's a good idea to have a pragmatic and general-purpose tool for filesystem-based dataflow programming. It makes an excellent glue. I am not convinced that tool should be make. It's got a lot of warts.
It makes sense to me to use a specialized build system for each language. They can be faster and richer than something generic. People can live inside one programming language for decades straight, so optimizing their experience is worth it.
The problem with Make (or any build tool) is that few developers care about how their project is built. They want to focus on writing their code, push a button, and get into testing it. That's a fair enough attitude.
But the problems with build tools are insidious. It's basically trivial to build a small project, so to start with, everyone assumes it's a nothing problem, and spend no effort on it. Then as the project grows, over months and years, the problems slowly accumulate. Each time some feature or corner case is added, the build gets slower, more complex, and cruftier. Eventually someone decides to take a look inside, and recoils in horror...
But it's not Make that's the problem (or any build tool). It's just the fact that building big projects is a tricky problem, and requires just as much effort to solve elegantly as any other tricky problem.
I worked at a big bank that had a problem with their Make-based build system taking multiple hours (sometimes days) to build some of their projects. They spent millions on trying to replace it with some Java monstrosity. I spent a few months rewriting their Makefiles, and the build times shrank to less than 1% of what they had been.
Make was the best tool for the job, because the job was vast, and had many, many corner cases. Make is a general purpose language, and is fairly unopinionated about how you use it to solve problems. All the "Make replacements" I looked at were far too opinionated to be elegantly bent to the task at hand. They were designed to look simple. That's great when your problem is simple, but big builds are not simple.
Yes. Place the secrets in a secured (symmetric encrypted) document. Then print the password for that document and hand it to a loved one, trusted family member/friend, or lawyer.
When I tried to introduce s-expressions to a DSL my co-workers nearly lynched me. The parenthesis were so violently hated I sunk into a deep hole and still haven’t came back out of it.
It really is kind of frustrating isn't it? Because anyone who's put any real effort into learning Lisp knows that the parentheses are not what's hard to understand, actually. You don't even have to read them, really.
So when people complain loudly about parentheses in Lisp that just tells me they probably never made any real effort to learn it, and are actively opposed to trying.
It's not a productive starting point for a discussion.
They're not. But they're also not required to flaunt their own ignorance as though it's a well-formed opinion. Also, in a professional setting, I find it a bit unprofessional and rude to poo poo technical ideas over trivialities.
> But they're also not required to flaunt their own ignorance as though it's a well-formed opinion.
I don’t like the parenthesis. I’m not flaunting anything. Is it “flaunting” to calmly and respectfully share an opinion? To you it’s trivial, to me it’s not.
It seems like you’re interested in creating conflict with people that don’t like a thing that you like. Which, I would cast this behavior as unprofessional, tbh.
I'm not a fan of the parenthesis either, but when I learned about s-expressions and how lisp programs are also a data structures that piqued my interest and helped me look past them.
I question people's judgement who can't look past the syntax when there is a very good, and interesting technical reason behind them.
Code as data is interesting, but mostly orthogonal to S expressions. For example, Prolog code has the same property without S expressions, and more esoterically TeX (which is succinctly explained to a lisper as programming with defmacro but not defun).
Though it's just more powerful in Lisp precisely because the code is just lists in a language designed around working with lists.
So you can actually leverage this property in Lisp without the code becoming inscrutable for it, which in my experience doesn't usually happen in other languages.
> I question people's judgement who can't look past the syntax when there is a very good, and interesting technical reason behind them
The list of things that are interesting is endless, though. I see something I’m turned off by, I move on. There are plenty of valuable things to spend time on.
What's an A-expression? I tried looking it up but didn't find anything.
I have heard of M-expressions but never of an actual implementation. Scheme also seems to have some SRFIs involving alternate syntax that's whitespace dependent, like SRFI 119 (wisp), SRFI 110 (sweet-expressions or T-expressions), or SRFI 49.
Yeah it is a personal issue, there are people who really like Lisp syntax (I'm one of them). That doesn't mean anything bad, of course, some people like dark text on a light background, others light text on a dark background. Everyone is different and that's what makes the world so awesome.
I see something like:
some-var: I64 := a * b - c ^ d ^ e;
...and my brain gives out, while I find:
(let ((some-var (- (* a b)
(^ c d e))))
(declare (type I64 some-var))
...)
How do you look at the latter and know the code structure? Are you counting parentheses? Or are you relying on conventions around white space indentation. If the latter, are you not concerned a misplaced parentheses might make the code different from it appears? There could be bugs not shown in the indentation.
Most lispers I know code in eMacs or other smart editors that provide auto code formatting and colored parens. But why not just make the indentation (or whatever that you really rely upon) the actual syntax, so there CANNOT be hidden bugs of that sort?
Edit: to expand on this, I think it is no coincidence that most lisps remain untyped to this day. Strong typing is about having the compiler enforce type rules so you the developer can’t fuck it up. Weak typing is more convenient, but ultimately a source of bugs. Lisp has, effectively, weak syntax. I don’t like weak syntax for the same reasons I don’t like weak typing.
> But why not just make the indentation (or whatever that you really rely upon) the actual syntax, so there CANNOT be hidden bugs of that sort?
That's Python. When whitespace matters, any aesthetic reformatting mistake can change the program's meaning. With s-expressions this cannot happen. A lisp code parser is completely deterministic regardless of where the newlines, spaces, and tabs occur. You can remove all the newlines from a 10,000-line Lisp program and the compiler will parse it exactly the same as if it were formatted aesthetically.* You can also write a simple program that takes that godawful one-line program and reformats it aesthetically however you like--the meaning won't change.
IOW in Lisp the aesthetics of the source code do not determine its meaning; aesthetics and meaning are orthogonal properties and you are free to adjust the two independently. This is also somewhat true in languages like C, but rather than several special-case punctuation characters, in Lisp there's only one: The parenthesis. Lisp is thus similar in spirit to HTML where semantics and layout are [mostly] independent.
* With a few obvious exceptions like EOL comments, and newlines that are part of quoted strings.
> any aesthetic reformatting mistake can change the program's meaning.
Why would you just randomly change indentation? On the contrary, I don't want the indentation to say something else than the code actually does.
> You can remove all the newlines from a 10,000-line Lisp program and the compiler will parse it exactly the same as if it were formatted aesthetically.
> Why would you just randomly change indentation? On the contrary, I don't want the indentation to say something else than the code actually does.
Because the user may want the development environment to display snippets of code in various places: REPL, debugger, code browsers, inspectors, various editor types, ...
In a Lisp system the code can be data and text. Code formatters can reformat code depending on user preferences, device types (color, font, ...), view sizes, ...
In Lisp often code gets generated (for example via 'Macros') and this code will be automatically layouted in various view (different widths, different fonts, different detail). Code can be small or large, the system may abbreviate parts, which one can expand, if necessary.
Source Code is not necessary static text in a file system. Code can just be list-based data structures and layout is fluid.
In Common Lisp the formatted output of code is also user extensible/customizable, a 'pretty printer' is a part of the language spec:
Use an editor that auto-inserts parens and that indents the code correctly. Now nothing bad can happen. And the parens are used to edit code structurally.
re typing: Coalton brings Haskell-like typing on top of CL. https://github.com/coalton-lang/coalton/ Other lisps are typed: typed racket, Carp… and btw, SBCL's compiler brings some welcome type warnings and errors (unlike Python, for instance).
Well I don't like indentation-based syntax because when I write code with such syntax down in a notebook I have this bad tendency to drift to the right as I go lower down the page. And then if I go training or something and return the drift causes me to lose the structure of the code.
With round brackets on the other hand, well... I think my brain just works with round brackets. Like my mind is coded in Lisp or something, I dunno. It would explain why I'm so slow: GC pauses. My mind should really be ported to SBCL or something.
You're clearly the exception though. It's almost always just ignorance in my experience. That's why I used the word probably. I assume if someone asked you what you thought about Lisp, you'd have a lot more interesting stuff to say than just "ew, parentheses", right?
That's my point, is all.
As for readability, I don't think it's trivial at all. I just don't think syntax has all that much to do with it. It plays a role, but a very minor one compared to overall code quality. In other words it's syntax that's mostly a trivial point, not readablity.
One undeniable advantage of Lisp in terms of syntactic readability though is that other languages always end up piling on more syntax over time as the language gets older. That's by far my least favourite thing about Rust for instance, even though I do love the language. There's always some new syntax, keyword, or position that an existing keyword can suddenly go in. The longer I go without actively using Rust, the more work it is to start again, because I have to go learn all this new stuff now. And syntax always takes a while to feel intuitive, at least for me. But it's still a lot easier than grokking new semantics and paradigms. I still don't have a good handle on async rust.
If someone told me to go read some ALGOL 68, I could probably do just fine because there's nothing semantically unfamiliar about it compared to something like C. But if someone gave me some Haskell written as sexps I would be utterly lost despite the syntax being perfectly comfortable to me. Because I never quite grokked typed FP.
No, the horrible syntax of lisp is pretty much all I have to say on it, honestly. I don’t care for lisp for the same reason I don’t care for perl: write-only languages are a bigger hinderence to software maintenance than whatever advantage might be obtained. And the advantages of lisp have long since been obtained by other languages.
Lisps are not write-only. They're easy to read precisely because there is no inscrutable syntax. Every pair of parentheses has a first element, which tells you what they're doing. The parentheses denote a scope in which to look for arguments.
Conventional style guides also make the readability practically a trivial issue because you end up indenting arguments such that the structure of the program is reflected in the indentation, because each S-expression within a parenthesized S-expression is itself an AST node. Writing readable Lisp is just a matter of reflecting the AST's structure in the indentation, which most people do in other languages anyway (to some degree).
> the advantages of lisp have long since been obtained by other languages.
Clearly not, since most other languages are still not S-expression-based. (Though, admittedly, some other advantages have been copied in other languages.)
I did it once in an internal UI because we needed to rapidly expose some functionality that could be composed/piped in complex ways and it would take a while to implement a normal UI
Was met with skepticism, esp around the engineering/maintenance overhead, until I told them the parser took me a couple hours to write and was only a hundred lines of code or so
It is really fun (sad) to see this, I've seen it so many times myself too. You show how something would be with s-expressions, compared to something, and all they can focus on is how many parenthesis there are. But when you sit down and count, they're the same amount as the code was when it wasn't s-expressions, just in different locations. And when you remove the parenthesis, they can kind of understand the code, kind of.
One option that might be suitable for a DSL, is implicit parens based on whitespace. A newline opens a new paren, and the paren closes when it reaches another line with the same indentation e.g:
(defun factorial (x)
(if (zerop x)
1
(* x (factorial (- x 1)))))
could be rewritten as
defun factorial (x)
if (zerop x)
1
* x (factorial (- x 1))
I doubt that SRFI 49, or any other proposal I've seen online, has been battle-tested.
I've written thousands of lines of Scheme using my own preprocessor, and it's my favorite code to look at. I prefer Haskell, for incredible ease of parallelism and a deeper mathematical foundation.
The two features I look for in a reduced parenthesis syntax (like looking for the bone marrow in a beef stew recipe) are:
1. Some constructions begin doubly parenthesized. One needs a symbol to represent the missing object one parenthesis in. I use $.
2. One can write more expressive lines with a flavor of open paren that autocloses at the end of the line. I use |.
Whitespace significance might be the choice even more controversial than Lisp parens. For myself, I appreciate both, but those that do not, really do not.
Probably there are more Python users out there than combined users of all lisp-like languages, which I guess would mean people are less scared of white-space significance than s-expressions :)
The things I'm looking for in language syntax these days are
1. That it's unambiguous enough that my editor can format it correctly every time (provided the code is correct, obviously). Indentation-based languages like Python fail this.
2. That its elements and keywords are distinct enough that my editor can apply colours on it. I sometimes feel that S-expression languages fail at this because of their small number of distinct keywords, but that might not be true.
Besides these two points, if the language is not a joke language, it's probably fine.
But it's what ambiguous about Python's syntax. And I can make it a bit worse:
if a:
print("foo")
if b:
print("bar")
This is not a huge problem, but problems with syntax never are (in non-joke relatively modern languages). But it's still something I prefer that languages fix in their syntax if I get to have a choice.
The example you just posted is a syntax error, as it should be. And I still don’t get what is ambiguous about the syntax, the specification clearly states how indentation corresponds to a syntax tree.
I disagree with this take. I have found immeasurable advantages to working with non-IDE/non-hold-my-hand-editors when programming. The separation of concerns (editing text versus compiling and running a program) are valuable to me as a developer. I remember as a child learning code in environments like BASIC, Pascal, TCL, Inform6, etc. And knowing that I could use my preferred text editor that I had a mental model about was a big win. I could lookup references in a book and learn easily. In contrast to IDEs which always got in my way confusing the heck out of me when really I just wanted to type examples from the books I got from the library.
Call me old fashioned but I have a hunch that the separated and minimalist approach has value in learning programming. To this day I still find doing things with minimalist tools I am far more productive than my counterparts who hunt and peck through menus and mouse clicks. A big advantage I think it this separation and minimalism primmed my brain to see way to compose tools and ideas instead or relying on what the system tells me I can and cannot do.