Skip Navigation

InitialsDiceBearhttps://github.com/dicebear/dicebearhttps://creativecommons.org/publicdomain/zero/1.0/„Initials” (https://github.com/dicebear/dicebear) by „DiceBear”, licensed under „CC0 1.0” (https://creativecommons.org/publicdomain/zero/1.0/)S
Posts
5
Comments
11
Joined
7 mo. 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 likefor (&(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 currentfor (&(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

     rust
        
    match (left.next(), right.next()) {
        (Some(l), Some(r)) => {}
        (Some(l), None) => {}
        (None, Some(r)) => {}
        (None, None) => {}
    }
    
    
      

    turns into either the inconsistently ugly

     rust
        
    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

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

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

  • 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 === 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.

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

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

  • Programming @programming.dev

    Fixed Points and Strike Mandates - Paul Khuong: some Lisp

    pvk.ca /Blog/2012/02/19/fixed-points-and-strike-mandates/
  • 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).

  • Rust @programming.dev

    Patterns for Modeling Overlapping Variant Data in Rust

    mcmah309.github.io /posts/patterns-for-modeling-overlapping-variant-data-in-rust/
  • 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:

     rust
        
    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:

     rust
        
    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:

     rust
        
    fn is_second_num_positive(input: &str) -> bool {
        let n = input.split(',').nth(1).unwrap();
        n.parse::<i32>().unwrap() > 0
    }
    
      
  • Programming @programming.dev

    This Overly Long Variable Name Could Have Been a Comment | Jonathan's Blog

    jonathan-frere.com /posts/names-vs-comments/
  • I can imagine Berdly Deltarune trying to explain this to Kris and Noelle in roughly this tone of voice.