- 8 Posts
- 15 Comments
The fact that
x += ymodifieslists in place might be surprising if you’re expecting it to be exactly equivalent tox = x + y.
shape_warrior_t@programming.devto
Rust@programming.dev•things rust shipped without (2015)English
0·1 year agoThere was a recent langdev Stack Exchange question about this very topic. It’s a bit trickier to design than it might seem at first.
Suppose we require a keyword – say
var– before all binding patterns. This results in having to write things like
for (&(var x1, var y1, var z1), &(var x2, var y2, var z2)) in points.iter().tuple_windows() {},
which is quite a bit more verbose than the current
for (&(x1, y1, z1), &(x2, y2, z2)) in points.iter().tuple_windows() {}.
Not to mention you’ll have to writelet var x = 0;just to declare a variable, unless you redesign the language to allow you to just writevar x = 0(and if you do that, you’ll also have to somehow support a coherent way to expressif let Some(x) = arr.pop() {}andlet Some(x) = arr.pop() else {todo!()}).Suppose we require a keyword – say
const– before all value-matching patterns that look like variables. Then, what’s currentlymatch (left.next(), right.next()) { (Some(l), Some(r)) => {} (Some(l), None) => {} (None, Some(r)) => {} (None, None) => {} }turns into either the inconsistently ugly
match (left.next(), right.next()) { (Some(l), Some(r)) => {} (Some(l), const None) => {} (const None, Some(r)) => {} (const None, const None) => {} }or the even more verbose
match (left.next(), right.next()) { (const Some(l), const Some(r)) => {} (const Some(l), const None) => {} (const None, const Some(r)) => {} (const None, const None) => {} }and you always run the risk of forgetting a
constand accidentally binding a new match-all variable namedNone– the main footgun that syntactically distinguishing binding and value-matching patterns was meant to avoid in the first place.Suppose we require a sigil such as
before one type of pattern. Probably the best solution in my opinion, but that’s one symbol that can no longer be used for other things in a pattern context. Also, if you’re already using sigils before variable names for other purposes (I’ve been sketching out a language where a pointer variable$xcan be auto-dereferenced by writingx), doubling up is really unpleasant.…So I can understand why Rust chose to give the same, most concise possible syntax for both binding and value-matching patterns. At least compiler warnings (unused, non-snake-case variables) are there to provide some protection from accidentally turning one into the other.
I was thinking of the three legal states as:
- not logged in (
nullor{isAdmin: false, isLoggedIn: false}) - logged in as non-admin (
falseor{isAdmin: false, isLoggedIn: true}) - logged in as admin (
trueor{isAdmin: true, isLoggedIn: true})
which leaves
{isAdmin: true, isLoggedIn: false}as an invalid, nonsensical state. (How would you know the user’s an admin if they’re not logged in?) Of course, in a different context, all four states could potentially be distinctly meaningful.- not logged in (
My preferred way of modelling this would probably be something like
role: "admin" | "regular" | "logged-out"
or
type Role = "admin" | "regular";
role: Role | null
depending on whether being logged out is a state on the same level as being a logged-in (non-)admin. In a language like Rust,
enum Role {Admin, Regular}
instead of just using strings.I wouldn’t consider performance here unless it clearly mattered, certainly not enough to use
role: number,
which is just about the least type-safe solution possible. Perhaps
role: typeof ADMIN | typeof REGULAR | typeof LOGGED_OUT
with appropriately defined constants might be okay, though.Disclaimer: neither a professional programmer nor someone who regularly writes TypeScript as of now.
a === breturns true ifaandbhave the same type and are considered equal, and false otherwise. Ifaisnullandbis a boolean, it will simply return false.
I would certainly rather see this than
{isAdmin: bool; isLoggedIn: bool}. Withboolean | null, at least illegal states are unrepresentable… even if the legal states are represented in an… interesting way.
shape_warrior_t@programming.devto
Python@programming.dev•A quick language learning program I made.English
0·1 year agoNice! Some feedback (on your Python, I don’t speak Greek):
- In Python, the idiomatic way to name variables and functions is
snake_case– for example,greekTranslationshould begreek_translation. (EDIT after seeing your most recent reply: good luck with that when it comes to Python :) ) - You’re currently recomputing
reverseTranslationevery time the user requests an English-to-Greek translation. Unless you’re planning to modifygreekTranslationwhile the program is running, it would likely be more efficient to makereverseTranslationa global variable, computed just once at the start of the program. - The control flow in the main program could be a bit more clear:
- The condition in
while optionSelection == <'practice'/'translation'>makes it look likeoptionSelectioncould change to something else inside the loop, yet you never do so. You could just writewhile Trueinstead. - Instead of putting the “Please select either practice, translate, or exit” check up front, it would likely be more maintainable to add it as an
elsebranch to the mainif-elif-elifchain. That way, if you added in a new option, you would only have one place in the code to modify instead of two. (Also, instead ofx != a and x != b and x != c, you could writex not in [a, b, c]). - Speaking of the
if-elif-elifchain, you could replace it with amatchstatement, which would remove the repetitions ofoptionSelection ==.
- The condition in
Here’s how I would write the control flow for the last section:
optionSelection = input().strip().lower() match optionSelection: case 'practice': while True: practiceGreek() print('') print('Would you like another? [yes] [no]') print('') selection = input().strip().lower() print('') if selection != 'yes': break case 'translate': while True: translateToGreek() print('') print('Would you like to translate another phrase? [yes] [no]') print('') selection = input().strip().lower() print('') if selection != 'yes': break case 'exit': print('') print('Thank you for using the Greek Practice Program!') print('') sys.exit() case _: print('') print('Please select either practice, translate, or exit') print('')(You could probably pull the
while Trueloops into their own dedicated functions, actually.)Hope that helps. Feel free to ask if any of this doesn’t make sense.
- In Python, the idiomatic way to name variables and functions is
shape_warrior_t@programming.devto
Programmer Humor@programming.dev•Object oriented programming in Python be like:English
0·1 year agoEven regular Rust code is more “exciting” than Python in this regard, since you have a choice between
self,&self, and&mut self. And occasionallymut self,&'a self, and evenself: Box<Self>. All of which offer different semantics depending on what exactly you’re trying to do.
shape_warrior_t@programming.devOPto
Game Development@programming.dev•I'm making a game called In-Ring Out-Ring, here's a demoEnglish
0·1 year ago…Right, I kind of forgot that the directory structure for my repo doesn’t need to look anything like the directory structure of the releases. Thanks for prompting me to think about that.
shape_warrior_t@programming.devOPto
Game Development@programming.dev•I'm making a game called In-Ring Out-Ring, here's a demoEnglish
0·1 year agoThat was certainly not the first piece of feedback I was expecting to get, but it is one that’s good to take into account! I forgot that what seems obvious to me might not be obvious to people who’ve never seen the game before. I’d like it if players could learn the full game without resorting to textual tutorials, so I’ll have to find ways to teach things more clearly, but for now I’ve updated the README to hopefully make things more clear. Thanks for the feedback!
Interesting way of handling project vs global scope:
Some package managers (e.g.
npm) use per-project scope by default, but also allow you to install packages globally using flags (npm install -g). Others (e.g.brew) use global scope.I like the idea of allowing both project and global scope, but I do not like the flags approach. Why don’t we apply a heuristic:
If there is a
.sqlpkgfolder in the current directory, use project scope. Otherwise, use global scope.This way, if users don’t need separate project environments, they will just run
sqlpkgas is and install packages in their home folder (e.g.~/.sqlpkg). Otherwise, they’ll create a separate.sqlpkgfor each project (we can provide a helperinitcommand for this).Seems rather implicit, though, especially if the command output doesn’t specify which scope a package was installed in. If a user moves to a subdirectory, forgets they are there, and then tries to install a package, the package will unexpectedly install in global scope (though this particular version of the problem can be solved by also looking in parent directories).
shape_warrior_t@programming.devto
Game Development@programming.dev•[Devlog] What's Next for A Survival Game? I Need Your Ideas!
0·1 year agoPlayed a few rounds just to get an idea of what the game’s like. Seems interesting. It took a while to actually figure out what controls were available and what the cards might do. Sometimes the stickmen would group up in the left corner and stay there for a good while, and there was seemingly nothing that could be done about it. The mechanism for scrolling felt a bit awkward to me, especially since the screen scrolled by such a large amount per click. Just some general notes about my experience, take them into account in whatever way best suits your game’s vision.
shape_warrior_t@programming.devto
Programmer Humor@programming.dev•Learning to program in rustEnglish
0·1 year agoCan’t resist pointing out how you should actually write the function in a “real” scenario (but still not handling errors properly), in case anyone wants to know.
If the list is guaranteed to have exactly two elements:
fn is_second_num_positive_exact(input: &str) -> bool { let (_, n) = input.split_once(',').unwrap(); n.parse::<i32>().unwrap() > 0 }If you want to test the last element:
fn is_last_num_positive(input: &str) -> bool { let n = input.split(',').next_back().unwrap(); n.parse::<i32>().unwrap() > 0 }If you want to test the 2nd (1-indexed) element:
fn is_second_num_positive(input: &str) -> bool { let n = input.split(',').nth(1).unwrap(); n.parse::<i32>().unwrap() > 0 }
shape_warrior_t@programming.devto
Programmer Humor@programming.dev•RFC 2119, the audiobookEnglish
0·1 year agoI can imagine Berdly Deltarune trying to explain this to Kris and Noelle in roughly this tone of voice.






As the addendum to the paper points out, this is more like insertion sort (with
iandjin a confusing order) than bubble sort. The actual “important” part of the algorithm happens whenj < i. (In fact, I’m quite certain there aren’t even any swaps whenj >= iafter the first outer iteration.) (I think the page about people misremembering bubble sort is also worth looking at.)Honestly, I think this might actually be “better” than bubble sort, in the sense that at least it’s incredibly easy to remember and not that hard to get correct. The only place where you could realistically mess up is confusing the relative order of
iandj– and any amount of nontrivial testing will immediately show the error, since it reverses the sort order. I’d probably reach for this if I, for whatever insane reason, had to code up a sorting algorithm by hand for some task whereO(n^2)sorting was acceptable performance-wise. “Sort a list of 10 items in a very primitive programming language”-type deal.(Now that I say that, I’m kind of tempted to use it in some example program for my own in-development programming language, which currently doesn’t have a builtin sort function…)