Chig Beef

Posted on

Perfect Class Syntax, Every Language Falls Short

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

``````class Ball:
self.x = x
self.y = y

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

Pretty simple, now let's create the same class in 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.

``````type Ball struct {
x float64
y 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!

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

``````func createBall(x, y, radius float64) Ball {
fmt.Println("Created Ball")
}
``````

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.

``````class Ball {
x float64
y 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?

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

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

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

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

Or, alternatively

``````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?

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

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.

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

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

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 I'm creating an interpreted language inside of my game. 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.