this post was submitted on 02 Jan 2025
17 points (100.0% liked)

programming

250 readers
2 users here now

  1. Post about programming, interesting repos, learning to program, etc. Let's try to keep free software posts in the c/libre comm unless the post is about the programming/is to the repo.

  2. Do not doxx yourself by posting a repo that is yours and in any way leads to your personally identifying information. Use reports if necessary to alert mods to a potential doxxing.

  3. Be kind, keep struggle sessions focused on the topic of programming.

founded 2 years ago
MODERATORS
 

Hello all, i'm trying to figure out a language syntax and I keep going in circles, maybe some opinions from other devs may help.

For a given programming language what is the most efficient & cleanest way of describing a variable, type, etc?

Here is a list of ones I have considered. (psudocode)

C style (structs and return type prefix), pros: simple, really succinct, cons: annoying lexing because return type is ambiguous.

struct Vec3 {
  f32 v[3];
}
struct Player {
  char *username;
  Vec3 pos;
  Color appearance;
}
Player[] login(Player p, char *password) {
...
}
Player me("me", Vec3(0.0,0.0,0.0), Color(255, 0, 0));
login(me, password);

Rust style (function prefix, arrow return type), pros: separation of data and implementation makes composition easier. Cons: not very intuitive for learners.

struct Player {
  username: str,
  pos: Vec3,
  appearance: Color,
}
impl Player {
  fn login(self, pass: str) -> Vec<Player> {
  ...
  }
}
let me: Player = Player {username="me", ...
me.login(password);

Typescript style (similar to rust but without arrows, also OOP). pros: simple, easy to understand. cons: OOP can cause problems

class Player {
  username: string;
  pos: Vec3;
  appearance: Color;
  constructor(username: string, pos: Vec3, appearance: Color) {
    this.username = username;
    ...
  }
  login(password: string):Player[] {
  ...
  }
}
let me: Player = Player(...
me.login(password);

Fortran style (types below definition). Pros: clear whats going on? (maybe too used to fortran weirdness). Cons: extremely verbose.

type player
  string :: username
  real(kind=32), dimension(3) :: pos
  integer(kind=8), dimension(3) :: appearance
contains
  procedure :: login
end type player

player function init_player(username, position, appearance) result(this)
      string :: username
      real(kind=32), dimension(3) :: position
      integer(kind=8), dimension(3) :: appearance

      this%username = username
      this%position = position
      this%appearance = appearance
end function

function login(this, password) result(players)
  class(player) :: this
  string :: password
  player, dimension(:), allocateable :: players
  ...
end function
...
player :: me
me = Player("me" ...
me%login(password)

Go style, types after like rust/typescript but no :, interfaces instead of OOP, prefix types on the left of functions. Slices also backwards. Pros: simple, cons: syntax is backwards and weird, implementing a tupe would be annoying.

type Player {
  username string
  position Vec3
  appearance Color
}
func (p Player) login(password string) []Player {
...
}
me := Player("me" ...
me.login(...

Let me know what you think, thanks all!

you are viewing a single comment's thread
view the rest of the comments
[–] invalidusernamelol@hexbear.net 2 points 5 months ago* (last edited 5 months ago)

With Python it's all about the function signatures. Also since everything is dynamic, you can alias a signature (the commented lines) to describe a quick and dirty structure to the expected data, then later come in and implement that class if need be and you don't have to change your hints.

Since Python is dynamic though, it gets a bit grumpy about the order of type declaration and type hints. That's what the string hint is in the Vec class (the mul function is returning a type that isn't fully defined by the time the interpreter reads the function header). String hinting defers that type hint to the end of header and signature parsing.

The other really cool thing is the nested hints, e.g. list[int, int, int] to specify a data structure. That means that your linter will know that x[1] is an integer or whatever type you specify.

Edit: that last note only works with the immutable tuple since the list is mutable it only looks at the first assigned class

Another note: the @dataclass() decorator is actually not a type definition. It's a built in wrapper function for Python classes that scaffolds the boilerplate for you. Since every class needs __init__, __str__, __repr__, and __eq__ implemented, that wrapper/decorator just constructs those on the fly using the class attributes defined under the class header.

This is a very large abstraction that's meant to make your Python class definitions more DRY and especially for simple classes like this just show whoever is reading your code the important bits (attributes and types)