Perfect Class Syntax, Every Language Falls Short
Tags: go, csharp, python, kotlin
Published: 2024-03-27 00:11:36

## Intro
Have you ever loaded data from a CSV, and needed to create a class that can describe each row of that CSV? You've got 30+ columns, and now you're getting into your favorite language, you're so ready to make that class. You start to realize that this just sucks, writing massive classes sucks. Now you're really questioning everything, why are we here, just to write classes.
If you've read any of my other posts, you might've picked up on my focus on languages, so how would we fix this with language design?

## Where Most Languages Go Wrong
Let's make a ball class in a few languages. This class is going to have an x coordinate, a y coordinate, a radius, and it will also have a method to move the ball. Let's implement that in Python

```python
class Ball:
    def __init__(self, x, y, radius):
        self.x = x
        self.y = y
        self.radius = radius

    def move(self, dx, dy):
        self.x += dx
        self.y += dy
```

Pretty simple, now let's create the same class in C#.

```C#
public class Ball
{
    public float x;
    public float y;

    public Ball(float fX, float fY)
    {
        x = fX;
        y = fY;
    }

    public void move(float dx, float dy)
    {
        x += dx;
        y += dy;
}           
```

So what's wrong with these?
Well, we're writing the same thing twice! look at the Python implementation, we pass `x`, `y`, and `radius` into the class, and then assign it to `x`, `y`, and `radius`. What a waste of characters! You may think this is stupid, but now imagine you've got 30 properties. In Python, the arguments for `__init__` would be 3 page widths, and then you would have 30 lines! And in C# we would have the same issue, but twice!

## Golang & Kotlin
Let's see how Golang and Kotlin has created a fix to this.

```go
type Ball struct {
	x float64
	y float64
	radius float64
}

func (b *Ball) move(dx, dy float64) {
	b.x += dx
	b.y += dy
}
```

That looks nice, right? We're not doing the same thing twice!

```kotlin
data class Ball(val x: Float, val y: Float, val radius: Float)
```

Look, I don't know Kotlin, but I remember seeing this and thinking "wow, what a great idea."

## What's The Problem?
Let's have a look back at the Golang example, and now we can imagine that we want a print that we're creating the object, well now we have to create some sort of constructor function.

```golang
func createBall(x, y, radius float64) Ball {
	fmt.Println("Created Ball")
	return Ball{x, y, radius}
}
```
look how much we're repeating now! So now we have the real issue.

1. When we want a very static experience, we often have to write the same thing twice or three times
2. As soon as we want constructor logic, no matter the language, we are forced into more redundant logic

## The Solution
As someone who has designed a lot of languages in my spare time, I can't _not_ design a solution.

```go
class Ball {
	x float64
	y float64
	radius float64

	init(b Ball) Ball {
		println("Created Ball")
	}
}
```

What's good about this solution? Well, we can imagine when we call the `Ball.init` function, that a zeroed ball is constructed, such that x is 0, y is 0, and radius is 0. We then pass this `Ball` into the `Ball.init` function, run the code, and return it. What would calling this function look like?

```go
ball = Ball()
```

There is no duplicate logic here, but there are two more issues I want to address, and how we should fix them.

## Changing Properties
Let's say we want to create a ball with 3 as its `x`, well, that's simple.

```go
ball = Ball(3)
```

Now if we're to do `y`, there are 2 possible solutions.

```go
ball = Ball(0, 3)
```

Or, alternatively

```go
ball = Ball(y: 3)
```

This makes it easy t target the specific property you want to change. The second option is extremely useful for changing one property on an object with 30 properties.

Now what about defining default values?

```go
class Ball {
	x float64: 3
	y float64: 7
	radius float64

	init(b Ball) Ball {
		println("Created Ball")
	}
}
```

That wasn't so hard, was it? Now when we create a new `Ball`, it will automatically have `x` as 3, and `y` as 7. `radius` will stay as 0.

## Pointer Methods
This is the second issue, and a personal annoyance (especially against Python). I hate implicit pointers, which is why I like Golang's syntax as such.

```go
func (b *Ball) move(dx, dy float64) {
	b.x += dx
	b.y += dy
}
```

You can see that we pass `b` into the function as a pointer by the obvious `*`. In our solution, we want to make it obvious when we're doing this as well. Now, I would probably just end up at the same solution as Go, but here is an example for the `move` function.

```go
class Ball {
	x float64: 3
	y float64: 7
	radius float64

	init(b Ball) Ball {
		println("Created Ball")
	}

	(b *Ball) move(dx, dy float64) {
		b.x += dx
		b.y += dy
	}
}
```

## Conclusion
I think for all practical purposes, we've made the most efficient class definition, or at least the concept. The ideas can be used, regardless is squirlies, methods inside the class declaration, and so on.
I'm not thinking about this for no reason, if you read my [recent post](https://dev.to/chigbeef_77/writing-a-language-in-a-game-3egb) I'm creating an interpreted language inside of [my game](https://chigbeef.itch.io/compnerdsim). Hopefully, one day we can all create our classes and structs much easier. None of this means that the above languages have bad solutions, but in my mind, this is the best solution.