@buherator Could you please elaborate what you'd rather see? imo, if-let and let-else are perfectly reasonable keyword combinations, combining a refutable match with a assignment. Without these constructs, assignments can only be irrefutable patterns.
if-let - run the block if the let assignment matches the refutable pattern
let-else - run the else block if the let assignment doesn't match the refutable pattern. the else block has to diverge (can only evaluate to the ! type aka "never" type)
refutable pattern: a pattern which might not match, such as the pattern Some(val) matching against a Option<u8>
irrefutable pattern: a pattern which always matches, like a simple name, or a tuple pattern matching against only that tuple (unpacking the tuple)
considering this, what keyword would you propose? how would you express "refutable pattern matching assignment" as a longer yet not overly verbose keyword?
also, the & just means "reference", and i haven't had that mess with searching for a error
https://tech.lgbt/@risottobias/113724437738947086
I would suggest verbosity and not overloading a line with multiple things.
even when rust tries to be slightly readable, it basically becomes brainfuck. https://github.com/asterinas/asterinas/blob/main/kernel/src/fs/ext2/fs.rs#L54
(brainfuck hello world: ++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.)
it's not even that rust contains an annoying amount of extra descriptive punctuation that is highly relevant to the error (compared to, e.g., typescript which says things like const),
it's that it overloads a line's return value several times over like overusing ternaries.
yes, condition : true ? false; is succinct, but it is not maintainable.
@laund @buherator a criteria for maintainability would be something like:
if your line has an error, it must be at most in one of two punctuation spots (like := vs =), or maybe one of 3-4 keywords.
any language that encourages massive one liner truncation is asking for an error somewhere along character 80 which was supposed to be a | instead of .unwrap()
because .promise().unwrap().must().shift_left().catch() is soooooo readable...
@risottobias @buherator i feel your comment stems from a misunderstanding of what kind of language Rust is. You're approaching it eith a very statement-based mindset, but Rust is a expression language. It doesn't have ternaries, its normal "if" is an expression, meaning it can evalue to a value:
let mut num = 0;
if some_cond {
num = 5;
} else {
num = 10;
};
can be written as:
let num = if some_cond { 5 } else { 10 };
which is far more verbose and readable than JS/TS ternaries. This same way also works for match, loop, etc.
Your linked example may be somewhat dense, but its rather clear as soon as you understand that all { } blocks are expressions. i'm having a hard time reading the comparison to brainfuck as anything but bad-faith.
@laund @buherator ternary-like source code minification is basically the example.
rust makes dense code, not readable code.
the minimization of misplacable but important characters (like ' for lifetimes) is similar to using very short, undescriptive variable names.
type is indeterminate to the reader on source alone,
python lambda-like one-liners seem encouraged,
large parts of common libraries have junk like this: https://github.com/serde-rs/serde/blob/master/serde_derive/src/bound.rs#L41 (where multi line mutations are used)
density of code and obfuscation of the type and mutability seem pretty frequent.
it's a language where squinting to find the misplaced period vs comma would be quite annoying.
@buherator hmm, i think to clear up why the order is that way around it makes sense to elaborate a bit on a core part of the syntax which is very useful in some cases but can be ignored a lot of the time:
every assignment in rust is pattern matching:
let a = 5;
^ this is a irrefutable pattern
which means you can do this:
let Vec2(x, y) = some_vec2;
^^^^^^^^ this stores the first field of the Vec2 struct in x, and the second in y
its just that most assignments are, as i tried to explain, irrefutable patterns. they can only be used to unpack/destructure a specific type.
thats why the order of if let <pattern> = <value> { <block> }
is like that.
its analogous to for <pattern> in <iter> { <block> }
in for loops like this you also have the pattern first then the value, and then the block.
your example is a interesting proposal, but it adds a new syntax for assignment, whereas the current if-let and let-else syntax keeps the same syntax for assignment: let <pattern> = value
except its not really minification. its the removal of redundant information which is not actually relevant to understand the code.
for your lifetime example: the tick may be a small character, but within thw syntax, in the places where it can be used, its not actually misplaceable due to the surrounding syntax, making a more verbose way of marking a word as a lifetime name quite redundant.
While yes, in many cases the concrete type isn't directly readable from source, you don't actually want to have to specify the type everywhere as that would get extremely verbose, and its always easy enough to find.
"multiline mutations", which i would call "method chaining", is a common pattern in many languages and proven to work well.
yes, if you try to write finished Rust programs without invoking the compiler once, and in a basic text editor, you'll likely have a bad time.
Its a language built with workflows in an IDE and utilizing compiler feedback in mind, and thats fine.
@risottobias @buherator i'm also very surprised that you think a language like Rust, where mutability has to be declared explicitly both for owned variables are references (pointers) obfuscates the mutability of things.
i can imagine some of the functional languages take this further, but amongst other multi-paradigm especially systems languages i cannot think of any language where mutability is treated as clearly and obviously as in Rust. the most widely used languages are much worse in this aspect (JS, Python, C, C++, Java, ...)
@laund @buherator that would be because rust uses an explicit keyword, longer than a single character, and thus does one piece correctly, by expressing the type of the variable (mut).
It's true, it isn't completely unusable like misplacing a parentheses in lisp,
But it's mostly the fact that it uses some keywords instead of only special characters that gives it a redeeming, if annoying, level of inconsistent and paper cutting design
@risottobias @buherator i really do wonder what sort of mindset one has to approach Rust from to call it inconsistent. It's, syntactically, a far more consistent language than any other i know.
Things like everything being expressions with the semicolon making a expression evaluate to () instead of a specific value and separating expressions into statements is a level of consistency i constantly wish for when writing other languages.
Other langs may have some destructuring/unpacking, but in Rust every assignment is pattern matching (let, loops, even function args).
While writing Rust, i think theres only one part where i couldn't follow and appreciate the reasoning for a sytanx being the way it is: cfg macros. I feel those should be using operators for and, or, not, etc. instead of what looks like function calls.
What i quite understand tho is that its a language which doesn't really follow the same logic as many of the popular langs, i had to actually learn the syntax instead of just seeing similarities
@buherator ah, i think i finally understood part of your thought process:
you read "if", expect a normal "if <condition>", and then get whiplash from seeing a assignment in the <condition> place.
Thing is, a refutable assignment can fail, which you can think about as the condition evaluating to "false".
In words, i would put it as: "if this let matches and the assignment succeeds, do <block>"
or in the case of let-else "let name be val unless it doesn't match, in that case do <block>"
just trying to explain why I don't have an issue with the readability of if-let, and maybe why it was decided to implement it like this.
btw: the syntax you expect kinda exists as a macro: `if matches!(value, <pattern>) { <block> }` but it can't do assignments.