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!

all 20 comments
sorted by: hot top controversial new old
[–] Llituro@hexbear.net 9 points 5 months ago (1 children)

The best syntax is the one that allows you to accomplish what you'd like to. Anything else is simply taste. My preference is for Rust style because I appreciate the clarity and find the style relatively intuitive, but Rust makes that syntax choice in conjunction with being related to C and MLs. There's also the style of Pascal that you can see in languages like Elixir. Different still.

[–] zongor@hexbear.net 3 points 5 months ago* (last edited 5 months ago) (3 children)

Right now I’m only focused on look and taste. For reference the closest to what the end product is it’s going to be a typed lua, really simple and easy to understand. I looked at ML, Haskell but they seemed a bit too esoteric for a new dev. Pascal I totally forgot about, should look at that. I also like rusts syntax but I’m not sure if the way the implementation syntax is obvious.

Edit: looked at pascal and this looks similar to an early draft of mine lol. I think I moved away from the style of syntax (I used ‘as’ instead of ‘of’) was that it made the declaration really long and the type being at the end of it means you would need to wait to allocate memory until the whole line is parsed in.

[–] xj9@hexbear.net 4 points 5 months ago* (last edited 5 months ago) (2 children)

The research I've read on programing HCI seems to indicate that programming languages in general are not intuitive for learners (with the fascinating exception of APL, which borrows from maths syntax. Apparently control flow is not intuitive to non programmers at all lol). ML like syntax is common and likely to be familiar to programmers, but IMO what is "best" is really more about familiarity so just follow your bliss.

~~P.S. I'll link to sources when I'm not on mobile~~

Video summary

References (also linked in the video description): Notation as a Tool of Thought; Pane, 2001; Miller, 1981.

[–] Llituro@hexbear.net 4 points 5 months ago

with the fascinating exception of APL

i'm friends with a math phd who is a huge APL truther, i'm not sure i can share this information, he might become insufferable. that's really neat though.

[–] Faresh@lemmy.ml 2 points 5 months ago (1 children)

Apparently control flow is not intuitive to non programmers at all lol

If it is the control flow that is not intuitive, what about functional programming languages? Code written in those languages, generally has a more declarative style to it. What do beginners think of those languages? What sets APL apart from functional programming languages? Is it the fact, that beyond not relying heavily on control flow, that it also doesn't stray away too far from math into "computer-specific concepts territory"?

[–] xj9@hexbear.net 2 points 5 months ago

I updated my comment with links to some research and a video where a guy talks over the concepts in some detail!

[–] Zvyozdochka@hexbear.net 3 points 5 months ago* (last edited 5 months ago) (1 children)

For reference the closest to what the end product is it’s going to be a typed lua

Have you looked at Roblox's Luau? It might be a good place to get some ideas from.

[–] zongor@hexbear.net 2 points 5 months ago* (last edited 5 months ago)

Luau seems to be a typescript/rust mixture with a pinch of Fortran. It’s serviceable but it’s limited in its primitive types. That’s a different issue I’m still thinking of but that’s not for this one.

[–] xj9@hexbear.net 2 points 5 months ago (1 children)

Updated my other comment with these links:

Video summary

References (also linked in the video description): Notation as a Tool of Thought; Pane, 2001; Miller, 1981.

[–] zongor@hexbear.net 1 points 5 months ago

These look great, thanks!

[–] makotech222@hexbear.net 4 points 5 months ago

Letting my avatar speak for me

[–] xj9@hexbear.net 3 points 5 months ago (1 children)

Personally, I like functions that operate on plain objects. I tend to avoid creating classes unless the language forces the issue. Elixir makes me happy in this regard. Gerbil scheme does some of the same. BQN takes things to an extreme that I rather enjoy as well, but we're firmly in uncommon territory at that point XD

[–] zongor@hexbear.net 1 points 5 months ago* (last edited 5 months ago)

BQN

Neat! Ill have to read up on this, I haven't used a APL-like language before. The closest thing to APL or BQN ive used is Uiua if you have heard of it.

[–] kleeon@hexbear.net 2 points 5 months ago (1 children)

I find how Odin does things pretty interesting. It is somewhat Pascal-like but has some differences. In short:

Variable declaration looks like this:

someNumber: int = 123

You can drop the type though and the compiler will infer it for you:

someNumber := 123

In order to declare a constant, you replace = with : to get this:

aConstant: int : 456

And with type inference:

aConstant :: 456

Odin has functions(or procedures in Odin parlance) as first-class citizens, so procedure declaration is simply a constant declaration:

doStuff :: proc(stuff: int) -> int {
   return stuff + 1
}

This kind of syntax has been first used in Jonathan Blow's programming language, so there is another example if you want to learn more. Though it's not public yet unfortunately

[–] zongor@hexbear.net 2 points 5 months ago (1 children)

I have taken a look at Odin, its interesting, its kinda like a bunch of different languages, its got the postfix types like typescript, rust, etc but other structures similar to Go or Pascal.

[–] kleeon@hexbear.net 2 points 5 months ago

Yeah I've been checking in on it for a couple of years now and it seems like the most promising C replacement so far

[–] invalidusernamelol@hexbear.net 2 points 5 months ago* (last edited 5 months ago) (1 children)

I kinda like Pythons type hinting system. If you use mypy or any static analysis tools you get the benefits of static type checking with the flexibility of being able to explicitly revert to dynamic typing when you want.

from dataclasses import dataclass
from typing import TypeAlias

@dataclass(slots=True)
class Vec:
    "Dataclass implementation of Vec"
    x: float
    y: float
    z: float
    
    def __iter__(self) -> int:
        for v in [self.x, self.y, self.z]:
            yield v

    def __mul__(self, other: "Vec") -> "Vec":
        return Vec(*[a*b for a, b in zip(self, other)])

#Simplified TypeAlias Vec
#Vec: TypeAlias = tuple[float, float, float]


@dataclass(slots=True)
class Color:
    r: int
    g: int
    b: int
    a: int

#Simplified TypeAlias Color
#Color: TypeAlias = tuple[int, int, int, int]

@dataclass
class Player:
    username: str
    position: Vec
    appearance: Color

    def logon(self, password: str) -> None:
        ...

me = Player("me", Vec(0,0,0), Color(100, 55, 55, 0))

#with TypeAlias instead of implemented types
#me = Player("me", (0,0,0), (100, 55, 55, 0))

me.logon("password")

[–] zongor@hexbear.net 2 points 5 months ago (1 children)

This actually might be close to what im looking for, kinda like this or how haskell does it where it declares the typeclass above the function and then the function signature and body below

[–] 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)