Python with Braces
github.com
github.com
Code is so incredibly hard to mentally grasp and every mental overload should be omitted to reflect on the logic.
There is a reason why there is one code basis and this should always be curated by a linter to uniformly enforce a standard.
There is still plenty of room for style and code organization.
I witnessed first hand many trench wars around seemingly small things like curly brackets in IF statements dealing with the question of one white space or none, because it appealed to personal preferences and before ESlint people would go to great length reformatting hundreds of LoCs just to get their right feeling of code syntax.
Weird. And git -diff was massive, as well as the code reviews.
(Un)Happy times. :D
You can configure ESLint to use prettier, but in my experience it's usually easier to just run ESLint separately to prettier.
>Maybe we'd have editors and viewers where you could configure the syntax however you wanted
I took this to mean that, in this fantasy universe, you could make any source file look however you want. Like tabs vs spaces and pure html vs html-with-css, this is about separating meaning from presentation. Is there a good reason to force the same visual representation on everyone?
Leaky abstractions. If there are two different visual representations then sometimes someone using one representation will need to think about the other.
(This isn't the best example of what I'm getting at, but it's easy to grasp. A stronger example would be the use of ternary operators with multiple lines of JSX. There are many "micro-syntaxes" or "micro-layouts" that help readability and form common patterns in code bases which don't play well with tabs-for-indents, so when you use tabs, you have to train your team to develop and accept novel versions of these "micro-layouts".)
But if the syntax was separate from the underlying representation, couldn't you just have your editor open it the way you want?
I’m all for keeping consistent flat utf-8 files. I’d hate for my code ultra simpleminded, possible to pen test with a pen and paper, python and sql code to be wrapped in a god awful xml or json or proprietary db markup and object hierarchical model of what code should look like.
Like I imagine trying to check in a jupyter notebook but worse.
For instance tabs vs spaces was decided and text editors accommodated this and despite what may be someone’s personal preference a uniform decision was made.
Humans can learn. We should use accessible formats and push for standards and keep those readable.
It would definitely be the norm that there were languages that were dark or light mode, and both sides would be convinced they were right.
Do you group things like:
do_stuff()
more_stuff()
other_stuff()
moar_function()
Or: do_stuff()
more_stuff()
other_stuff()
moar_function()
Or: do_stuff()
more_stuff()
other_stuff()
moar_function()
Grouping code by a single blank line can make a big difference. This is a bit of a silly example, but there's tons of not-so-silly examples. You can't really represent that in an AST.Line length is another. infinite line length doesn't work as screens aren't infinitely long. Automatic wrapping doesn't work because you want to break at specific points. An example is something like:
window = XCreateWindow(display, XRootWindow(display, screen),
center_x, center_y, size, size,
4, // border width
vinfo.depth,
CopyFromParent, // class
vinfo.visual,
CWColormap | CWBorderPixel | CWBackPixel | CWOverrideRedirect,
&window_attr);
Cramming as much as possible on as few lines as possible is just not going to work well.There's tons of cases.
That's why no one does it. Because it just won't work. Everyone will hate it.
<visual-group>…</visual-group>
Also, I wish that at least in current editors there would be a way to render \n\n as a half-height line. Full-height empty lines are too bold.
Automatic wrapping doesn't work because you want to break at specific points
You really want to have a set of buttons that switch between:
- a line of arguments
- a block of arguments
- a line/block of only non-default valued arguments
- arguments sorted by name
- …
And a hint on a default representation, with some default heuristics.How does it know that x, y, width, and height are semantically grouped and best put on the same line? It doesn't. (from the other comment)
Look higher, parameters can be grouped at the declaration level. <params><related-params name=“coords”>…</>…</>. Now you can render a nice frame around these in block mode. Or not, depending on local renderer settings.
Everyone will hate it.
There’s always a way to make everyone hate something, especially if the solution is clueless about its problem. It doesn’t mean it should be done this way. Experiment and evolution could make it work, we just have to let people try instead of dismissing it so confidently.
That said, if you use classes, descriptive names, and appropriate comments, it won't matter how you group because your code will be self explanatory.
Finally, with today's wide-screen monitors on desktop, line length is less of a worry. Problem only arises when reading code on mobile devices.
The last workplace I was at had a soft wrap around 80 characters, but we upped that to 100 when functions and methods became almost vertical.
Also, why does your comment have a paragraph break after each sentence except after the third? Because of semantics and the related readability. The same is true for source code. Readable layout does not depend on syntactical structure alone.
did you buy a large monitor just to full-screen a single text file?
window = XCreateWindow(display, XRootWindow(display, screen), center_x, center_y, size, size,
4, vinfo.depth, CopyFromParent, vinfo.visual, CWColormap|CWBorderPixel|CWBackPixel|
CWOverrideRedirect, &window_attr);
And that's just not better. It's much much worse. Only a human can judge where it makes sense to break things (or not break things).Completely forbidding "paragraphs" would be atrocious. Almost everyone will hate it. Just like everyone hates walls of text in comments.
And in the end a huge amount of complexity is added throughout the entire stack (from editors to grep to sed to code hosting) for very marginal gain with significant trade-offs.
1. Breaking at specific points is something that can be specified by the pretty-printer of the _viewer_ you are using. Think of existing auto-formatters and imagine that they're working over the view instead of the persisted form.
2. The AST can have pointers into advisory data (or the other way around, if desired, the "program data" can include the AST, but also other things as well) to note that there is an anonymous region here (C# and friends already have conventions for this _for the source code_ that Visual Studios understands - look at `#region` comments). This would let viewers choose their preferred representation for a region.
And including tons of extra info in the AST means you've just got the same as a text file, but in a more awkward and obfuscated format.
Also what you want can already be done right now. Converting your source code to AST and then formatting it how you want is already easy to do. But your coworkers will hate you, because you will lose all these small details that are actually quite significant. And there is no way to get them back once they're lost, so your coworkers all using some formatting tool is not going to help.
You could then decompile to some alternative syntax, but you'd lose any idiosyncratic formatting represented by the compressed diff.
Last I checked, Java AoT compilation precluded runtime re-optimization, though I presume they've fixed that by now.
Last I checked, they both used stack-based bytecode, which typically takes longer to JIT and results in slower native code than a compressed SSA / control flow graph (see the SafeTSA papers).
Though SSA is deferred to JIT/ILC instead. In either case you get the access to all the actual low-level bits when you need to. No other portable target lets you do that.
I don't see why it couldn't be done though, I think it just hasn't been a priority. Heck, you could have 100 different users collaborating in 100 different "languages", and so long as they serialized to the same AST and back, none of them would ever have to see the atrocious syntax which the other users prefer. Their editors and browsers could just render everything according to their users' preferences.
Edit: it appears that Unison has an issue for this feature: https://github.com/unisonweb/unison/issues/499
Note that I said 'statements', not 'expressions'.
A lot of the confusion here (and maybe yours, too) stems from this difference. In Rust, (almost) everything is an expression by default, and you turn it into a statement by adding a semicolon. This allows you (and the type checker) to very neatly distinguish between expressions and statements, which is great. It's a very nice and elegant approach imo.
Personally, I much prefer the design Go uses (where semicolons are implicitly added at the end of newlines following an identifier, numeric or string literal, keyword, or operator).
let x = {
3
};
let y = {
3;
};
assert_eq!(x, 3);
assert_eq!(y, ());
i think it'd also mean having to parse whitespace or newlines without something like that?Method chaining is also common in Rust, because builders are common, and chains of iterator adapters are common, and chains on monadic structures (option/result) are common, … having every line break implicitly insert a `;` would be horrid.
Plus I don't think I've ever seen a case where semicolons made the code harder to read (or write).
If you follow those rules, you will see that this:
if true;
{
fmt.Println()
}
else
{
fmt.Println()
}
Will get rewritten to: if true;
{
fmt.Println();
};
else;
{
fmt.Println();
};
And that won't work. That's why the braces need to be as "} else {". and "if .. {".It used to be that the compiler gave some pretty confusing errors about semi-colons on this, but it seems that's been improved now.
JavaScript has similar semi-colon insertion by the way, but with some different (more confusing) rules.
foo
.bar()
now you need to either add syntax to specify that you’re “continuing” (python), play tricks for the parser (Go), or have unreliable magic biting you in the ass half the time (javascript).What if the AST is persisted as S-expressions, but then you have a different syntax to edit it? Algol-ish, Pascal-ish, C-ish, Python-ish: choose your poison (or even support multiple poisons and let the developer pick the one they prefer?)
This was actually the original plan with Lisp. Lisp was originally supposed to have two syntaxes, S-expressions and M-expressions, with M-expressions being Algol-like. However, the implementation of M-expressions was delayed, and people got so used to using S-expressions directly, they decided M-expressions were unnecessary and they were never implemented in mainstream Lisp. They were implemented in the Lisp 2 project, but that ended up being an evolutionary dead-end; various attempts at the idea have happened since but none of them really took off.
Lisp purists will argue M-expressions are unnecessary and S-expressions are all you need. However, S-expressions can make the language more foreboding to complete beginners, and even among experienced programmers, a decent percentage find them seriously off-putting. Maybe if the M-expression idea had been pursued more seriously, Lisp might be more popular today.
I would argue that S-Expressions turned out to be extremely practical for developing Lisp software. A layer with different syntax makes it more complex to use.
> Maybe if the M-expression idea had been pursued more seriously, Lisp might be more popular today.
Maybe, maybe not. People have discussed it endlessly, but there is no conclusion.
Currently some people in the Racket community try to make Racket more "popular" (widely used, ...) by providing a new syntax.
When you start thinking of your program as a nested data structure instead of a text document, you start thinking more clearly - you can manipulate this tree representation directly. This makes molding your program significantly faster, closer to the speed of thought. Wholesale refactoring a function might only take a dozen keystrokes to move the forms (not the chars) around.
I have a hard time going back to the syntactic busywork of manually placing ascii characters like a caveman.
But lisp did not aim for that from the start, as the GP hinted McCarthy intended S-expressions as a data description form, m-expressions were supposed to be the executable language.
Many editors are already altering what is stored on disc before presenting it to you - type annotations, code folding, git info. I think we could do a lot if our default storage was the semantic representation of the code.
Imagine writing a blog about your favorite CSS features. I mean, if you use Word at all for writing, wouldn't that come handy?
But Word actually is not the primary goal here. grep or awk may be more important - I've created ad-hoc tools for ad-hoc quick-and-dirty tasks on code a number of times, and the fact that code is just text helped a lot. Taking that away would make me spend time on figuring out how to hook up text tools to that format.
> Many editors are already altering what is stored on disc before presenting it to you
True, but the difference is between being able to enrich commonly understood format, and not having a common format between different tools at all.
I do feel sad that we're still restricted to tools like grep and awk that treat everything as dumb text, and I think it holds us back hugely. I much prefer the powershell model of everything is an object. You can easily extend it with C#.
I appreciate that the unix philosophy has gotten us to incredible places, but I do wish we could rip off some of the training wheels and take advantage of the computing power that we have available to us that was just unthinkable. My mobile phone is more powerful than could possibly have been imagined when we make restricted itself to tab as a delimiter, and I'm currently using it as a coaster to save my table from water marks.
And you will have easier time getting your changes merged into dotnet/runtime than into Python.
Even simply "sharing" within your own systems, like copying blocks into notes or another program, would be a lot harder. Maybe I'm not knowledgeable enough here and this wouldn't be as thorny as it seems.
Your AST is what EMF calls a "model". By default the "backend" and ecosystem surrounding EMF is skewed towards Java for historical reasons, but there have been some prototypes with other languages as well. You can serialize your AST in any way you like, although by default it relies on XMI files. You can implement your own textual concrete syntax, or rely on a database. The EMF ecosystem has tools for implementing textual or "graphical" concrete syntaxes. You can combine them (e.g. usually a specific subset of your AST gets edited in a certain way that's best for your targetted end users). The ecosystem also has tools for performing comparisons and plugging them into your editing means.
Of course all of this tooling requires a lot more work than an LSP server.
I think, the reason it was never implemented was that more translation = more complicated debugging. It also means that programmers have a more distorted and incomplete model of the program they are writing, i.e. more bugs.
NB. Lisp, as originally envisioned by McCarthy, had one more translation layer (the translated version had square brackets instead of the parenthesis), but it didn't take off for, basically, the same reason.
So... while I understand the benefits you see from doing what you suggest, I think that at the same time the downside makes this not worth pursuing.
.NET decompilers are common. I have built a few toy languages and compilers on .NET. For one of them, I could decompile CIL into my language. So, I could view .NET libraries from other sources in my language.
I think this is essentially the same idea you are proposing.
It only works if the languages are similar though. Going between F# and C# does not always work as well for example.
You are describing an entire industry of IDE Smell with an IDE monoculture.
Edit: I do agree and find your AST suggestion profound though!
I think we don't have any sort of flexible AST sort of thing because they're mostly not necessary. The hard problems of programming don't usually have much to do with syntax.
And if you're going to downvote, kindly explain why, thanks. I just want to know exactly how this thing is supposed to work...
I may recall incorrectly but AppleScript may be an example: some file formats are serialized ASTs. The editor displays it as textual code. A downside of this is that you can’t save a syntactically invalid file.
Sure it's an intuitive way of representing your data. Is it the most appropriate though? See an example [0] about using Projectional Editing in order to use mathematical notations for formulas.
[0]: http://voelter.de/data/pub/gemoc2014-voelterLisson-MPSNotati...
There are visual programming languages that chain together blocks, instead of raw text.
> The hard problems of programming don't usually have much to do with syntax.
I guess it depends on how you define hard. You are clearly talking about "a singular issue that needs to be solved", which really only effects a single developer / team and, to a lesser extent, those that use that solution. But if you consider something like syntax, you're now talking about something that much a much smaller impact _per developer_, but has that impact on _every_ developer. The syntax issue may have a much larger impact overall.
% echo "nums 1 10 | filter even | to_words | map uppercase" | refab imagine
TWO
FOUR
SIX
EIGHT
TEN
% echo "with file '/tmp/top-ten-most-populous-cities.txt' do; cities = read; cities.each { |city| (city.name, city.utc_offset) }" | refab imagine
Tokyo, 9
Delhi, 5.5
Shanghai, 8
São Paulo, -3
Mumbai, 5.5
Mexico City, -6
Beijing, 8
Osaka, 9
Cairo, 2
New York, -5
For what it's worth, the tool isn't specialized for this, 'imagine' is just one of many prompts it can execute.Of course the execution is non deterministic and at the moment only works for simple things, but you can imagine as LLMs get more capable and more integrated with tools this will matter less and less.
I recently started writing a game in Godot. I don't know GodotScript, and I've found I don't like it very much in trying to learn. I turned to aider.chat to see if I could describe the functions, data structures, and systems I wanted and have it write them. I also tried writing in a more familiar language (...one with braces...) and having it translate those files.
It does pretty well, but it doesn't feel like software engineering. It's too hands-off and doesn't activate the same neurons. All the problem-solving and puzzle-solving is gone, and the successes are quite boring, and the failure modes are more irritating even if they're necessarily quicker to solve.
It's a weird experience. I'm moving so, so much faster than I would have on my own, but I don't enjoy it. It feels like cheating - I'm not actually ashamed of what I'm doing but I also won't take credit for writing the code.
However, what I'm getting at is this: If I could write the code in a syntax or even language that I prefer and have copilot or whatever translate it in near-real-time (without active prompting), that would be the best of both worlds. I'd still be a little sad at myself if I didn't learn the new language, but I also think this method would facilitate learning better than what I'm doing with aider (because I could see what my code turns into as I'm writing it, and learn that "translation").
Here is an example of GPT's output for Python with braces that was generated after just spending 10 seconds for the prompt:
def preprocess_braces(code: str) -> str:
lines = code.split('\n')
processed_lines = []
indent_level = 0
indent_str = ' ' # 4 spaces for indentation
for line in lines:
stripped_line = line.strip()
# Check for opening brace
if stripped_line.endswith('{'):
processed_lines.append(indent_str * indent_level + stripped_line[:-1].strip() + ':')
indent_level += 1
# Check for closing brace
elif stripped_line == '}':
indent_level -= 1
else:
processed_lines.append(indent_str * indent_level + stripped_line)
return '\n'.join(processed_lines)
# Example usage:
code_with_braces = """
def example_function() {
if True {
print("Hello, world!")
}
for i in range(5) {
print(i)
}
}
"""
processed_code = preprocess_braces(code_with_braces)
exec(processed_code) # This will execute the transformed Python code
print("Processed Code:\n", processed_code)Isn't this the fundamental problem with code generated by a statistical text generation algorithm?
In other words, "code" is short for encoding a solution. And to have a solution to encode is to understand the problem to solve.
Without understanding, statistical code generation is little more than a popularity contest.
In my case earlier today, it helped that it was a relatively simple function and I gave it a rather detailed natural language spec of what I wanted it to do. I totally could have written it all myself, but writing a natural language spec and getting GPT-4o to translate it to code is (depending on my mood) less mental effort than just writing the code directly.
To some, significant indentation is better.
Others — too used to braces — miss them dearly in Python.
Next ones, vie for the non-text source code, something to get us past these discussions altogether (editors working on .pyc files directly?).
For programs to be maintained, they need to be read, understood and improved. One—often undervalued—skill in programming is to write beautiful code, because that is more art than craft. And unfortunately, tools like Black prohibit the true artists from expressing themselves clearly with code formatting too. And to those, white-space or braces matters on a different level, and everything else is attempting to make up excuses for why one is better than other.
And while conceptual operations we do on the code seem simple on the surface, devising an editing tool that would do semantic operations on the AST is fricking hard and likely to be very non-ergonomic. Look at all the attempts to make code refactoring tooling: it's crazily complex and confusing that it's simpler to just go and grep for a string and fix anything you find.
As long as it's faster to use regular editing operations to shuffle code around, indent or unindent it (or wrap it with braces), tweak one thing here or there, simple text editors will mostly rule the world of programming.
Python's choice of representation is such that two different Python programs show zero differences under a white-space-suppressed diff.
The white-space suppressed diff is a useful tool for comparing programs when some sections of code have changed indentation but are otherwise the same; yet we cannot rely on it if we are using Python.
Python's syntax design is objectively poor on several purely technical points. In its favor, there are only handwaving pop psych arguments.
What’s the problem with having no redundancy? By the same logic, we should require numerals to be spelled out in words so the compiler can check if the programmer didn’t accidentally write a different number.
def fahrenheit_to_celsius(x): {
return (x - 32 [thirty two]) * 5 [five] / 9 [nine];
}If humans can do it, why wouldn't computer programs like interpreters do it?
yes, because in Python two programs with different indentation are not the same. what's the problem that you can't use tools intended for code without significant whitespace with code in Python?
I've seen both misplaced braces (akin to mis-indenting blocks in Python), indentation not matching braces and other problems of the same sort in non-Python code. Readers of the code would misunderstand the code when badly indented, and might introduce bad braces as well (not everybody uses automatic formatters and linters either, esp as they will sometimes "quickly" edit code in their non-usual dev environment). Not to mention that some "braced" languages allow having single-line blocks without braces.
It's also not true that this is the only way Python encodes structure: new blocks generally only start with a ":", and you've got control flow keywords that allow for new blocks to start. One could argue that's a great feature disallowing you from introducing confusing spacing without actually having a new block started.
While I am fond of applying many of double-entry-accounting principles in programming to increase trust in what we write, I believe that's much better done with unit-tests, which can more clearly demonstrate the expectations for any code and read more like documentation.
Do you think all syntax in programming languages should have some sort of extra validation built-in? Eg. you should type in a constant twice (declare it as `const int a = 5` and then you have to set the value later as `a = 5` or you get an error?)?
As I said above, people will always find an "objective" excuse why their preference is better. But I've seen bad-block-boundaries in Python as much as I've seen it in other languages which use explicit block boundaries like braces (and I've done more Python over the last ~20 years). I've heard this argument a gazillion times, but hundreds of bugs due to that have simply failed to materialize while working on large projects with tens and hundreds of people.
Getting indentation right is _really_ not that hard, just like getting braces right is not that hard. I've yet to find someone who prefers their code to not be indented at all, and only rely on braces — at least not in a team setting where you can't simply reformat all of it.
You have to consider ones you have not seen: the ones that are valid syntax, and thus invisible.
> (declare it as `const int a = 5` and then you have to set the value later as `a = 5` or you get an error?)?
For that, it would be more like assert (a == 5). It's not commonly done for constants. Neither the definition nor assertion are perturbed by significant whitespace, so it isn't the same.
If the constant only exists for clarity and not configuration (so the code cannot conceivably work with any other value), then the assertion would be warranted:
assert(a == 5); // Did you read the comment about not changing a?Sure, that's a fair point, but the same holds true for braces. One of those bugs I found in non-Python code was there for a couple of years (badly placed braces).
For the other topic, you were specifically praising the duality of braces and indentation to understand block boundaries. It's hard to come up with an example where everyone does the non-obligatory thing anyways (indentation in {} languages), since obligatory thing is, well, obligatory.
The ability for GCC and Clang to diagnose misleading indentation can be helpful; some programmers may wish to use it (and others may prefer to disable it). However, apparently it treats a tab as a number of spaces; I think that it should not do that, and using tabs on some lines and spaces on other lines to measure indentation within the same block, should always be considered misleading indentation (so if -Wmisleading-indentation is enabled, then it should always display a warning message in that case).
I think that to do this right, there would have to be a complex option in which you specify the tabbing style. -Widentation=<notation> where <notation> is some way of specifying the style.
There has to be allowance for alignment also. Any line, regardless of indentation method, can terminate in one or more spaces that exist for aligning the fist non-whitespace character with something in the previous line.
This must surely be a bad thing? How would this signal an unfortunate indentation change between:
if foo:
bar()
bar2()
And: if foo:
bar()
bar2()
???>Python encodes structure in only one way, using indentation
Except when you write a multiple line string using """ notation.
Or when you put things inside parenthesis, which i have seen as the preferred solution for method chaining.
Point is, python claims indentation is all that is needed, and then very quickly breaks it's own rule.
A preferred way to chain methods is to store values in descriptively named intermediate variables: it would also ease debugging as you'll get a direct pointer to the line that is problematic. Though, TBH, that's probably bad API design (I know it's common with DB query syntax, but that's attempting to turn SQL query construction into objects when they really aren't).
Python, like most languages, doesn't really stop you from doing crazy stuff. But also like most languages, it can be done really well.
As someone who has been diffing endless amounts of python through various diff tools, this is totally a non issue.
Python... I'd love if it shipped with a formatter that converted indents to braces, and then had an option for expressing indent as spaces (with number of spaces per indent) OR tabs (same, default 1); then still kept the braces.
While we're on the topic, if we store only syntactically valid programs, we can express diffs in terms of semantic refactoring rather than textual changes. This would enable stuff like preserving refactoring across merges, thereby bypassing conflicts that would arise under text merges. There are limits to this of course as you can still come up with conflicts, but anything to ameliorate the nightmare of manually fixing a textural merge.
However, I'm totally with you on having the editor show the code as you'd like. As much as I don't like tabs, at least the user could choose their preferred width for indentation. (A less disruptive Python-with-braces could be the editor showing braces but converting to spaces behind the scene.)
Such editors would remove some categories bikeshedding, but would add brand new categories of bikeshedding.
* Why did/didn't you add an empty line (EDIT: or whatever no-op visual equivalent) after that `if` block? My editor needs blocks to be separated a certain way to be able to display them nicely grouped in logical blocks! This representation of AST is so limited that we can't store such differences in style, we need editors that work at some super-AST level!
* What do you mean my code is an unreadable mess with hundreds of operations? All I see in my editor is a nice single operation "copy fields from class X to struct Y". If your editor can't detect such an obvious thing from the AST and display it nicely, then find a better one.
* Some codebases not even bothering with functions because the founding engineers use a specific IDE that just lets them group code arbitrarily (no no that's not reinventing functions, that's what progress looks like!), and you can't use your favorite editor because that's not on the scope of that editor, so the editor's author politely tells you to use that other editor you dislike if you really need such a thing.
* I can probably come up with more scenarios if I spend more time thinking about it. And there's probably also more petty scenarios that I can't even imagine.
I'm not saying such editors wouldn't be nice. Everyone has their own preferences, and some people might work better this way. Just, don't expect them to reduce the amount of petty bikeshedding in projects, much less eliminate it.
Lispy languages have structural editing tools that make it a lot like working directly on an AST. It's a delight when you get used to it.
The spacing/linebreaks are all just auto formatted and mostly an afterthought. It would only be one step further to present the code with the users choice of block start/end sequences.
But do imagine a world were changing the name of a field in a struct results in a single diff message, 'struct foo field bar changed to baz'. And where a change set to to a library can be mechanically applied to to code that depends on it and it just works.
if foo == 1: # {
print "one foo"
# }
It also supports PASCAL style begin/end: if foo == 1: # begin
print "one foo"
# end
And it's fully internationalized, even supporting mixing multiple languages: if foo == 1: # beginnen
print "one foo"
# 終了
In fact, you can even mix styles: if foo == 1: # begin
print "one foo"
# }
Or optionally leave one of them out, or spell them any way you like, if you feel like it: if foo == 2:
print "one foo"
# fi
Even verbose COBOL style: if foo == 1: # perform conditional foo value check
print("one foo")
# end perform conditional import sys; print(sys.executable)
x = 5; # this is fineI felt this way until I engaged in code with very horizontal coding styles. Semicolons make it extremely easy to visually break up sequential statements from expressions consuming multiple lines.
I'm not sure what you mean. Could you give an example?
var list = obj.FindDataNear(a, b...).Select(i => obj.GetPosition(i))
.Where(...long lambda...)
.MoreLINQ()
.ToList();
So with the style I used, the semicolon is superfluous due to indentation. Are there styles where the semicolon is useful?If the current line starts on the same line that the previous expression started on, then a semicolon is inserted at the beginning of the current line. There are also some messy rules about opening and closing braces being inserted.
So, this:
do expr1
expr2
do expr3
expr4
Ends up being parsed as though it were written like this: do { expr1
; expr2
do { expr3
; expr4
}
}
If the next line is indented, it's just taken as a continuation of the previous line's expression, so nothing needs to happen. for x in *.txt; do echo $x; done
for x in *.txt
do
echo $x
doneMake python a lisp - indentation is just the number of brackets.
Hy - http://hylang.org
(if x
(if y
(if z
foo)))
versus if (x) {
if (y) {
if (z) {
foo;
}
}
}Try it like this:
if (x)
{ if (y)
{ if (z)
{ foo; } } }
The rules are a little different. We have two-space indentation, like what is favored in Lisp, but there is a space after the opening brace. For symmetry, we put spaces between the closing ones. It works best if we don't "cuddle" the opening brace but put it on a new line.There is a rhyme and reason to it, and consistency.
I've written some C programs that way, though not recently and can't point to any online examples.
However, I also wrote, and maintain, the Yacc grammar file in TXR Lisp in this style (just the actions in the grammar portion). For whatever reason, it works well in grammar files.
{} are braces
This is English English
Swahili looks unfamiliar to me. Because I didn't grow up in southern Africa. Don't conflate subjective familiarity with objective simplicity - any "unfamiliar" concept only reflects on you, not your subject.
I was pointing out that there is yet another alternative.
I prefer lisp syntax over indentation aware (e.g. python) over block syntax and at the bottom ones that force semi colons on you (the end of line is sufficient) - so the OP syntax for me is a massive downgrade of the language.
Somewhere in that list I would add APL near the top but can't quite categorise it.
Antoine de Saint-Exupéry
Unfortunately, we still live in an era where humans have to adapt to technology rather than the other way around. From my perspective, this is particularly true for programming.
For me, Python is a good (though not ideal) mix of simple syntax and power. The language is characterized by low redundancy, meaning it uses fewer unnecessary characters like semicolons or curly braces to mark the end of a line. If a human can recognize the end of a line without special characters, then the compiler should be able to as well.
As someone with ADHD, I find it particularly difficult not to get distracted by these and other superfluous details. These small distractions add up and can become very burdensome. Interestingly, I found it easier to program in Assembler and Modula than in languages like C++ (MSVC), PHP, or JavaScript – at least as long as the projects were small.
Even a brief look at Rust’s syntax causes me almost physical discomfort, no matter how great, powerful, and useful the language may be.
For this reason, I almost exclusively use the terminal for emails, calendar, and programming, even though complex GUIs can simplify some tasks.
Although Python is not perfect in terms of syntax, it offers a good balance. Perhaps one day, before the perfect programming language exists, we will be able to use AI and ML to explain to the computer what a program should do with simple language (better than ChatGPT right now), just like Captain Picard. In fantasy, a few letters, punctuation marks, and some grammar is all that is needed. This may lead to inaccuracies in human-to-human communication, but that does not mean the same problems must occur in communication with an intelligent compiler.
Making syntax as “human-readable” as possible should always be the highest priority. We could unlock so much potential this way.
That’s a shame because you’re missing that these sigils actually have meaning there, because the semantics of the language are completely different: Python is statements-based with very limited scoping (global and function), Rust is expression based with block scoping.
As a result, blocks (paired braces) are a way to pack multiple statements into an expression e.g.
let v = {
let a = thing1();
let b = thing2();
thing3(a, b)
};
And `;` is not an alias for end-of-line, it’s a separator for statements.And not aliasing end-of-line to end-of-statement is relevant to rust being expression oriented, it’s very common for expressions to span multiple lines, in that case Python requires either wrapping the entire thing in parenthesis or escaping the EOL with `\`.
Could you find other ways to do this? Sure, but then you have to make other tradeoffs e.g. wrap everything in matching symbols à la lisp, or make statements into special cases à la Haskell.
This is a red herring. Haskell, CoffeeScript, Nim, Lean, etc. are expression-oriented and use indentation like Python, while C(++), Java, JavaScript, etc. are statement-oriented and use braces.
And in Python \n is not an alias for end-of-line, it’s a separator for statements.
I think there is a balance to strike here.
I often like to work with code by cutting and pasting sections around and then hitting the format hotkey to align everything.
I enjoy the guarantee that as long as the syntax is correct, it doesn't matter how I type out the code because I'll just hit the formatter hotkey immediately afterwards and it will apply the correct indentation and lay it all out nicely
Obviously that's impossible in Python and it makes working with it really frustrating to me, it feels so delicate, I almost don't want to touch the code because I'm always accidentally changing the indentation, it's a real limit of this "human-readable" syntax in my opinion.
I just tried it in vscode, it didn't work at all, copy pasted a single line to another function and it kept its indentation and vscode showed a pylance error about unexpected indentation.
I just have the official Python extension by Microsoft, perhaps some more configuration is required?
I don't really write any Python, I'm sure there is a way to improve the experience.
Skill issue. I just press my “paste with correct indentation” hotkey and move on.
I think it's interesting that you say this, and point it towards Python.
Personally, with Python's significant whitespace, I feel more constrained writing code in a style that I prefer, with the computer requiring me to adapt to it, compared to other languages. I see code with braces and semi-colons more freeing because I get more control over the line structure.
At the end of the day, it's all stylistic personal preference. Python isn't the evolutionary ideal form for programming languages, it's just what some people prefer.
Many people here argue that braces do provide some structure that helps in understanding and navigating the program. Python files over 100 lines with a lot of if-statements become syntactically unreadable.
So taking them away does not help.
Generally, minimalism (except for the Lisp-style one) is not always good. Python is called executable pseudo-code. Do academics use it to specify algorithms?
No, most still use some form of Pascal/Algol style syntax, which conveys the meaning much better.
Is the syntax not about 80% the same? And isn't most of that "same simple syntax" commonly used daily?
> Many people here argue that braces do provide some structure that helps in understanding and navigating the program. Python files over 100 lines with a lot of if-statements become syntactically unreadable.
That's probably True. But poor programming discipline and syntax that eases reading problematic code also contribute to this issue.
> So taking them away does not help. For me it actually promotes better coding style and hygiene.
> Generally, minimalism (except for the Lisp-style one) is not always good. The quote was about perfectionism, not minimalism.
This transpiles into Python. Okay. So for this to work in a collaborative environment, EVERYONE working on the code has to use it. Given how popular python is, and the natural inertia of programming languages, I simply don't see that happening.
The reasons for this are legion: From programmers simply being used to how python looks, over IDEs and linters that would have to be remade, all the way to automated tooling.
And even if all that were not a problem: This introduces one more step and one more element into the build chain. Which is something I can't see DevOps doing, for a, at the end of the day, purely cosmetic change.
[0] https://github.com/yairchu/awesome-structure-editors/blob/ma...
[1] https://news.ycombinator.com/item?id=9495493
[2] https://web.archive.org/web/20151023070244/http://joelburget...
IMHO, some level of linting should be required. I would often have to reject lengthy and excellent pull requests (PRs), just because autoformat was not pressed and three instead of four whitespaces are in kotlin code. That annoys everyone. Python does that right, code won't compile with the three version whitespace, which is easily spotted before the PR.
It has bugged me when using python interactively but fortunately ipython will ignore it.
Text editors are easy, but are waaaaay in the past. There could be much much better 3D like editors out there, but everyone is lazy to make one that would make your life much easier.
This.
I'd prefer using my own preferences instead of using a dumb formatter like prettier. Who cares some other dev in your team prefers 2 chars indentation? It should be an editor preference, not applied to source code.
Personally I would also prefer the IDE to do type checking as well so we can get rid of the TypeScript misery, but that's another discussion.
Now Ruby as his own defects of course. Static type can be integrated through Sorbet, but that still something out of the core for now. On that side, PHP had during the last years shown a far more remarkable pace in integrating many advanced feature for OOP (and functional stuffs are also progressing well). That said, the foundation of Ruby still win my preference in term of elegance and feeling of coherence.
I didn’t touch that much Python over the last few years. The only (significant) reason I would see it as opportune is its heavy use in ML and the like domains, and I didn’t have an opportunity to dig this domain much.
I don't use Ruby, so I don't know what happens in the Ruby world, but...
Doesn't this create a Holy War akin to C/C++'s fighting over where the opening brace goes (Current line vs next line)?
3.times do
print 'Welcome '
end
this works, but 3.times
do
print 'Welcome '
end
does not work. Same with curly braces. 3.times {
print 'Welcome '
}
and 3.times do
print 'Welcome '
endAlso TS has slightly more consistent syntax for return types than Python. ``` // TS
function fn(a: Type, b: Type) : Type {
}
// Python def fn(a: Type, b:Type) -> Type {
} ```
But I want python to have to close braces on as few lines as possible. Yes I'm a Black user and wonder how.
They could fix the latter, but the REPL is Python tooling so it is required to be awful.
Similar to OCaml which is very light on punctuation - code just ends up like a sequence of words, with an occasional comma and it's quite mentally taxing to figure out where the functions and parameters are.
> Black is the uncompromising Python code formatter. By using it, you agree to cede control over minutiae of hand-formatting. In return, Black gives you speed, determinism, and freedom from pycodestyle nagging about formatting. You will save time and mental energy for more important matters.
#define BEGIN {
#define END }
There is, to this day afaict, a warning suppression option in VisualStudio for pre-processor macros with unbalanced braces that uses exactly this as an example.Rather than a real parser. A real parser knows about the context it is in, and as long as the grammar is unambiguous about some character, it can be reused in as many distinct parser constructions as you like. A pure regex-based approach to a first approximation can not.
Whether or not it absolutely can or can't, without "approximation" and with regard to the exact re library in Python, would be something you'd have to work out through tedious effort. Regexes with backtracking can do a lot more than mathematical regexes, as well as all the other elaborations you could use. But even if Python's re can do it, you will be better off with a real parser.
The "correct" solution, since by design Python and Bython share the same AST, is to take some existing code that can parse and re-emit Python code without changing it, tweak the incoming grammar to use braces instead, then emit the AST back out as Python. Then you wouldn't have this problem, or the problem with comments referenced in the source code (line 109 of the file I linked, just up a bit), or any of the probably other many little issues that would arise on real code if you tried to put this in real use. The only slight complication I can see is there may be a slight issue with comments around the braces. I'm not sure, and I'm not going to sit here & work it out fully for an HN post.
Also, this is not a criticism of Bython. The author can do as they please with it. If nothing else you can always label this a prototype. Prototyping is good, right? Anyone who was rushing to slam this into production is just gonna learn a real valuable lesson worth its weight in gold, and not Bython's fault.
const foo = () => ({foo: 'bar'})
If you omit the parentheses it gets interpreted as a block with a label and a string literal: const baz = () => {baz: 'bar'}
https://astexplorer.net/#/gist/9a86e0b61fda312dce2ea9d10d37f...Pythons use their sharp, backward-curving teeth, four rows in the upper jaw, two in the lower, to grasp prey which is then killed by constriction; after an animal has been grasped to restrain it, the python quickly wraps a number of coils around it. Death occurs primarily by cardiac arrest.
They seem to have semi-lungs, with a right lung and a vestigial left lung
https://upload.wikimedia.org/wikipedia/commons/4/4d/Snake-an...
IMO lack of support for them (because of the lack of a good syntax) is the main downside of Python’s significant indentation.
def my_map_function(x): # do stuff
new_list = map(my_map_function, my_list)
?
Despite node feeling more verbose, I've found that I understand node easier than Python. It's definitely strange to me, because there can't be that much difference between the 2, but parentheses seem to make it easier to follow structure?
This seems contradictory. But I see the benefits. Perhaps braces and an official linter would be a more ergonomic approach.
It only makes sense if you're writing by hand on paper or designing a page, sure wherever you don't write is empty. But even then that's still visible space.
Then most language we currently use still rely on spaces in critical ways. `public function a()` and `publicfunctiona()`are not equivalent and there's no replacement character for the spaces.
Now, more power to you to like braces, same way some languages like dollar signs.
I still don't understand why some people don't use it. Even in non-whitespace sensitive languages, I need to see the whitespace so that I can tell if the formatting is off or not. Also make the code easier to read as I can literally see how far the code is intended instead of having to guess by amount of space.
Sure, automatic code formatting solves most of the issues but it is still good to have actual control over your whitespace.
[0] Except VIM which vexes me greatly. Might be one of the reasons whitespace sensitive languages are disliked by certain programmers?
Sure it does. See 'list' and 'listchars'.
>>> from __future__ import braces
File "<stdin>", line 1
SyntaxError: not a chancehttps://github.com/python/cpython/blob/main/Python/future.c#...
PR #1: Makes Bython self-hosting
self.onandonandon is a real pain along with copy.deepcopy
Troll. 100%. A good one at that.
Would you like in insane [0], FOMO-induced, Zen-contradicting `match` statement instead?
https://docs.python.org/3/library/functions.html#print
I suppose they could have given the option to continue using print as a statement if you don't care to override the default parameters, but having those parameters really simplified some options.
It certainly becomes a problem if you are looking at a 100+ line method/function, but with Python being as expressive as it is, a 100+ line method/function is a problem unto itself.
Otherwise, I've never had any issues with it — even with languages other than Python, I mostly rely on white space and alignment to understand the block boundaries, only resorting to reviewing the braces carefully when something is amiss.
>>> from __future__ import braces
File "<stdin>", line 1
SyntaxError: not a chance
>>> def foo(): #{
if True: #{
print("The future is now, old man")
#}
#}This is how Python got big: Discuss cute issues endlessly, pretend to be a funny, benevolent community. But real issues like performance, correctness or security are never addressed, and people who dare to mention them are punished severely.
The mailing lists are non-operational for political reasons and because most competent people have left. Patches have never been contributed via the mailing lists.
Python is the language for ML not because of the "community", which is an exploitative system where talkers who go on stage at a PyCon and never do anything get more money and worse, control who can earn money in the Python ecosystem.
It is the language for ML because Python had a decent C-API and a lot of scientific software has been written early on.