Skip to content

Appendix A. My view on language design

Intro

Each time a new language comes out or gets popular there are always things that bother me. The most weird thing I keep stumbling upon is abbreviations and naming conventions that got stuck from old languages.

Keywords & abbreviations

Even when abbreviations are not a thing, the language sometimes has other quirks. For example JavaScript has a clear keyword for making functions just called function, but in combination with other quirks.

If its not abbreviations, it something else like these keywords:

  • string (is never replaced by something like text for whatever reason)
  • int (is only sometimes referred to as an Integer although WholeNumber would even be better)
  • double (wait double of what? Double of 1, 10, 100, double portion)
  • float & decimal (could also be something like FloatNumber & DecimalNumber)

Testing & result processing

While testing, one of the things I really liked is the is runnable/testable documentation from rust. However I don't really like some of its abbreviations like fn & some other keywords.

That is not the only thing however as what I don't like is also how some languages are too focussed on exceptions and panick handlers. What I prefer however is not to have to deal with 2 ways of returning something. The Result pattern is a good way to deal with that, but unfortunately its not always straigt forward if the language already relies on exceptions.

The exceptions directly lead to my next point & that is how test methods are defined. Currently, the most common way is to write a test method and run one or more asserts at the end that crash if something is wrong. While this works, it not my most favorite design. I may not be able to change how its always has been done in an existing codebase, but I would rather see somthing like this:

namespace UnitTests;

// Yes, this is not valid C# because its expects class here!
// This is not simply a class however so why is it possible to create a record
// type, but a test still has to be annotated with attributes?
Test RotationTest : UnitTest
{
    static CharacterBody3D CharacterWith0RotationOnPosition0 => new()
    {
        RotationDegrees = Vector3.Zero,
        Position = Vector3.Zero
    };

    // Yes, this is not valid C# because its expects override here!
    // Another weird quirk, why do I have to override while actually implementing?
    // Even the extra `List<TestResultFunction>` might be unnecessary in some 
    // cases: `implement TestFunctions =>` since the base class already defines 
    // the type
    implement List<TestResultFunction> TestFunctions =>
    [
        Test(
            whether(() => CharacterWith0RotationOnPosition0.RotateY45DegreesTowards(new Vector3(10, 0, 0)))
            .matches(character => character.RotationDegrees.Y.ShouldBe(-90))
            .because(character => $"character's y rotation ({character.RotationDegrees.Y}) should change to -90 degrees")
            .when("target is right of player")
        ),
        Test(
            whether(() => CharacterWith0RotationOnPosition0.RotateY45DegreesTowards(new Vector3(10, 0, 5)))
            .matches(character => character.RotationDegrees.Y == -90)
            .because(character => $"character's y rotation ({character.RotationDegrees.Y}) should turn to -90 degrees")
            .when("target is almost right of player")
        )
    ];
}

Domain specific test prototype in Godot

The above is based on a working DSL (domain specific language) for my Godot game. It might also need some tweaking, but its the best I could come up with, without going too crazy with expressions and reflections. This is a work in progress however as I don't know where I am going to stumble upon as I progress further with the tutorial and its related game!