9
21
shape_warrior_t

@programming.dev

shape_warrior_t 34 points a year ago

I would certainly rather see this than {isAdmin: bool; isLoggedIn: bool}. With boolean | null, at least illegal states are unrepresentable... even if the legal states are represented in an... interesting way.

path: 0 18801213 18803188, hotness: undefined, score: 34, children: 4
shape_warrior_t 18 points a year ago

Even regular Rust code is more "exciting" than Python in this regard, since you have a choice between self, &self, and &mut self. And occasionally mut self, &'a self, and even self: Box<Self>. All of which offer different semantics depending on what exactly you're trying to do.

path: 0 18378410 18382852, hotness: undefined, score: 18, children: 2
shape_warrior_t 15 points a year ago

Can'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
}
path: 0 18071359 18075856 18077397, hotness: undefined, score: 15, children: 2
shape_warrior_t 12 points a year ago

There 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 write let var x = 0; just to declare a variable, unless you redesign the language to allow you to just write var x = 0 (and if you do that, you'll also have to somehow support a coherent way to express if let Some(x) = arr.pop() {} and let 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 currently

match (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 const and accidentally binding a new match-all variable named None -- 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 $x can be auto-dereferenced by writing x), 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.

path: 0 19153327 19158903, hotness: undefined, score: 12, children: 2
shape_warrior_t 9 points a year ago

I was thinking of the three legal states as:

  • not logged in (null or {isAdmin: false, isLoggedIn: false})
  • logged in as non-admin (false or {isAdmin: false, isLoggedIn: true})
  • logged in as admin (true or {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.

path: 0 18801213 18803188 18812369 18813225, hotness: undefined, score: 9, children: 2
shape_warrior_t 7 points 2 months ago

There's a good chance I can make use of str::substr_range on a current project of mine. I'll be storing some substrings from str::split, and now I can store the original string and ranges of indices into it instead of making an owned string for every substring.

path: 0 25407101, hotness: undefined, score: 7, children: 0
shape_warrior_t 7 points a year ago

Nice! 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, greekTranslation should be greek_translation. (EDIT after seeing your most recent reply: good luck with that when it comes to Python :) )
  • You're currently recomputing reverseTranslation every time the user requests an English-to-Greek translation. Unless you're planning to modify greekTranslation while the program is running, it would likely be more efficient to make reverseTranslation a 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 like optionSelection could change to something else inside the loop, yet you never do so. You could just write while True instead.
    • 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 else branch to the main if-elif-elif chain. 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 of x != a and x != b and x != c, you could write x not in [a, b, c]).
    • Speaking of the if-elif-elif chain, you could replace it with a match statement, which would remove the repetitions of optionSelection ==.

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 True loops into their own dedicated functions, actually.)

Hope that helps. Feel free to ask if any of this doesn't make sense.

path: 0 18459590, hotness: undefined, score: 7, children: 1
shape_warrior_t 7 points a year ago path: 0 19165423 19169963, hotness: undefined, score: 7, children: 2
shape_warrior_t 6 points a year ago

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 .sqlpkg folder 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 sqlpkg as is and install packages in their home folder (e.g. ~/.sqlpkg). Otherwise, they'll create a separate .sqlpkg for each project (we can provide a helper init command 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).

path: 0 18250087, hotness: undefined, score: 6, children: 2
shape_warrior_t 5 points a year ago

Played 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.

path: 0 18199633, hotness: undefined, score: 5, children: 1
shape_warrior_t 4 points 2 months ago

Just finished "version 0" for a very simple programming language yesterday, and I'm looking to work on "version 1" this weekend. Rust is truly fun to program in... although writing all the tests was not a very fun experience. But doing the work for that meant I was guaranteed to have a reasonably good grasp of the semantics by the time I finished.

Since it's Rust, there's no garbage collector -- heck, for this particular language, there's not even any memory allocation after startup. Instead you get a giant global array of "memory" to work with. The only data type is the 32-bit integer (and thus, using UTF-32 for strings is actually the """correct""" choice for this language). I'm using a vaguely Lisp-like syntax that revolves around {[a tree] formed from (brackets and whitespace)}... which was probably slightly easier to parse than more traditional syntax, but Rust's current lack of pattern matching "through" Vec (for example, letting you extract e from {a b [c (d e)] f} in one step) didn't always make it feel that way.

Nest up: adding while loops, exponentiation and bitwise operators, and most interestingly, string and character literals. (The char literals would just evaluate to their character codes, and string literals would only be usable in a specific write-to-memory construct.) And also expanding the standard library. I'd say "version 1" would really be the more properly "complete" version of the language. I also want to do a functional programming variation (ditching the global memory in favour of reference counted cons cells), and also the whole language is kind of just a stripped down version of another much bigger language half-formed in my head, but that's all probably for... not this weekend, at any rate.

path: 0 25197015, hotness: undefined, score: 4, children: 0
shape_warrior_t 4 points 8 months ago

Not very familiar with the libraries, and BB_C has already given a lot of more detailed feedback, but here are some small things:

  • Even without rewriting get_files_in_dir as a single chain of method calls, you can still replace
    if dir.is_err() {  
        return None  
    };
    // use dir.unwrap()
    
    with
    let Ok(dir) = dir else {
        return None;
    };
    // use dir
    
    In general, if you can use if let or let else to pattern match, prefer that over unwrapping -- Clippy has a lint relating to this, actually.
  • Although the formatting doesn't seem horrible, it doesn't seem like Rustfmt was used on it. You might want to make use of it for slightly more consistent formatting. I mainly noticed the lack of trailing commas, and the lack of semicolons after return None in get_files_in_dir.
path: 0 21925213, hotness: undefined, score: 4, children: 1
shape_warrior_t 4 points a year ago

a === b returns true if a and b have the same type and are considered equal, and false otherwise. If a is null and b is a boolean, it will simply return false.

path: 0 18808615 18812082, hotness: undefined, score: 4, children: 1
shape_warrior_t 3 points a month ago

As the addendum to the paper points out, this is more like insertion sort (with i and j in a confusing order) than bubble sort. The actual "important" part of the algorithm happens when j < i. (In fact, I'm quite certain there aren't even any swaps when j >= i after 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 i and j -- 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 where O(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...)

path: 0 25619394, hotness: undefined, score: 3, children: 0
shape_warrior_t 3 points a 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.

path: 0 18259629 18259979, hotness: undefined, score: 3, children: 0
shape_warrior_t 2 points a year ago

Probably a good idea, but not one that fits my current vision of the game, unfortunately. Especially since my main idea for adding complexity is increasing the number of sides (square->pentagon->hexagon), so each level already has a different-looking playfield.

path: 0 18257918 18258876 18259601 18260022, hotness: undefined, score: 2, children: 0
shape_warrior_t 2 points a year ago

I can imagine Berdly Deltarune trying to explain this to Kris and Noelle in roughly this tone of voice.

path: 0 18031785, hotness: undefined, score: 2, children: 0
shape_warrior_t 2 points a year ago

That 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!

path: 0 18257918 18258876, hotness: undefined, score: 2, children: 4
shape_warrior_t 1 point a year ago path: 0 18301028 18305447 18306122 18313582, hotness: undefined, score: 1, children: 0
shape_warrior_t 1 point a year ago

...Interesting idea. (And also interesting lore implications? Not that the game has any...)

My main thinking right now is: on startup, have the player on an empty field and force them to move (on both inner and outer "axes") onto a specially marked square (and press spacebar) to enter the main menu. (Controls and what they do will be explained in text.) This gets them accustomed to moving around and pressing the CONFIRM key for menuing.

Then have a special tutorial level to introduce the actual gameplay -- similar to the "real" levels, but much slower BPM and attacks are scripted instead of randomly chosen. I don't want to force anyone to complete the tutorial before entering the actual levels, but it would be the menu item that the player first lands on (aside from maybe the one for changing the controls to something less strange than A/D/J/L).

The tutorial level would require some more code, but at this point that might be worth it. (And also it would add another option for user-made custom levels if somehow people decide to edit those in.)

...Honestly, the one thing I'm most concerned about at the moment is getting players to read text, especially if it's off to the side to avoid covering the playfield.

path: 0 18257918 18258876 18259594 18260178, hotness: undefined, score: 1, children: 0

thanks for using Leebra!

go to feed...