Language X

Recently I’ve been thinking of what my ideal language would be like. I really like Java, but there are lots of little annoyances that you find in everyday usage. Something I’ve heard a few people complain about its verboseness, an example of this is that every method and member must be declared public or private, lest you accidentally leave it package private and have bad encapsulation. To combat this and other problems, here is a preliminary design for Language X:

Java-like syntax
Basically, method calls, operators, and other things from Java would work with Language X. Hopefully to the point that it would be completely source compatible and you could compile Java programs with X.
The only exception I will make here is that method and variable identifiers *must* start with a lower case letter, and type names *must* start with an upper case letter. This is a common style in Java and makes things much easier to read, however some code doesn’t keep to these conventions and it gets very annoying very quickly. Therefore, case is enforced for the first character.

Native code compilation
Nothing much to say here, basically it would compile to native code instead of some bytecode language like Java or C# do. The fact that it is not interpreted would hopefully make it much faster.

Access specifiers
In my opinion, Java access specifiers are done wrong. This is not because the levels are badly organised (package being more restrictive than protected can be nice in certain circumstances) but because the defaults are not sensible. In Language X:

  • Member variables would be private by default
  • Methods would be public by default (this includes getters and setters for properties)
  • Package would have its own keyword “package” (although this could cause slight problems with the grammar if “package” is also used for declaring which package something is in)
  • Protected would not include the package access level, and package and protected could be combined if desired.
  • Classes would be at the package level by default (I’m not completely sure on this one, public might be a better default)

Properties
A way of defining properties, to make getters and setters obsolete. To take a nonsensical example, defining properties would go something like:

class X
{
    // automatic getter and setter
    property String firstName get set;
    property String surname get set;
    // custom getter
    property String fullName get { return firstName + " " + surname; };
    int seconds; // just a member variable
    // custom setter
    property int hours
    get { return seconds / 3600; }
    set { seconds = value * 3600; };
}

Note that in fullName and hours, the backing variable is not used. The compiler should be able to detect this and automatically eliminate this if possible (however this could bring binary compatibility concerns).
Using these properties would be as follows:

X x = new X();
x.firstName = "Anthony";
x.surname = "Bryant";
x.hours = 3;
System.out.println("Full name: " + x.fullName + ", " + x.hours + " hours");

Default Arguments
Default arguments would exist in this language, and be done properly (unlike in C++). The reason languages like C++ do them badly is that you can only leave off the last argument(s), not arguments in the middle of the list. To correct this, default arguments would be given a name that must be specified by the caller if the default is to be overridden:

class X
{
    int foo(int x, int @y=5, int @z=7) { return x+y+z; }
}

which could be called as follows:

int a = x.foo(1, @z=9); // override only z, not y
x.foo(6, @y=a); // pass a variable into y, leave z as its default
x.foo(2010, @z=(12*(1+4)), @y=9*a);  // specify y and z in a different order

This could even allow the compiler to let default arguments to be specified anywhere in the parameter list, although it may be easier to read if they are only allowed at the end.

Tuples
Tuples would be supported in X, they would be simple data structures that are passed by value like primitive data types. This would allow you to easily return two values from a method without defining a new object to store them, or just swap two variables very easily. A method return type could be as follows:

class X
{
    (String, int) foo()
    {
        return ("a", 12);
    }
}

And it could be used like this:

X x = new X();
// creates a new tuple 'a' and assigns the result of foo() to it
(String, int) a = foo();
// creates two new variables, text and num, and assigns the result of foo() to them
(String, int) (text, num) = foo();
// swapping variables
int x = 12; int y = -1; int z = 5;
// creates a tuple containing y and x, and assigns it to another tuple containing x and y
(x,y) = (y,x);
// moves the values between x, y, and z
(x,y,z) = (z,x,y);

Closures
[Update 24/03/2010: This will need a rethink to work properly with generics. Partial application support would still be nice, but function type signatures may have to be changed.]
Closures in Language X would be haskell-like, and support partial application and currying. The best way to explain is by example:

{String -> int -> void} a = void closure(String text, int num)
{
 System.out.println(text + " " + num);
}

This can now be called as follows:

// calls the whole function at a time, returns void
a("text", 12);
// calls the function on only the first argument, producing another closure
{int -> void} b = a("text");

The internal structure of this sub-closure would be something like:

{int -> void} b = void closure(int num)
{
    a("text", num);
}

Within which it would store final references to ‘a’ and the string “text” that was passed into a when it was generated.
The way closures are defined also means it is possible to pass closures into other closures:

{int -> int -> {int -> String} -> String} a =
void closure(int start, int end, {int -> String} function)
{
    String s = "";
    for (int i = start; i < end; i++)
    {
        s += function(i) + (i == end - 1 ? "" : " ");
    }
    return s;
}

Also, methods can be assigned to closures, as a type of function pointer. This allows you to do:

class X
{
    void foo(String s) { System.out.println(s); }
    void bar({String -> void} f, String s) { f(s); }
    void baz()
    {
        bar(foo);
        {String -> void} func = foo;
        bar(func);
    }
}

An addition to this that could be useful under certain conditions would be the ability to cast a function which takes n arguments into a function which takes n+k arguments and does not use the last k. This would have to be an explicit cast, as it is a very obscure feature. However, it could be very useful if you want to use a function as an event listener but do not care about some of the parameters.

Immutability
Language X would also have compile-time support for immutability. This is one of the features I really like about C++, although making everything const-correct is very difficult to do, and the const keyword is too long to put in front of every immutable object. Therefore, references to immutable objects would be marked as such in the type declaration by a ‘#’ symbol. Method definitions could be marked by the ‘immutable’ keyword, meaning that this method can be called on immutable references to objects. Entire classes could also be marked with the ‘immutable’ keyword, signifying that all methods of this class are immutable. With regards to properties, getters would always be immutable methods, while setters would always be non-immutable. Immutability would work something like the following (properties are not used so that the point of immutability can be illustrated):

class X
{
    int x = 0;
    immutable int getX() { return x; }
    void setX(int x) { this.x = x; }
    // Compiler error: cannot alter a variable from an immutable method
    // immutable void alterX() { x++; }
}

This type of class would be used as follows:

X x = new X();
x.setX(4); 
#X immX = x; // casts x to immutable implicitly
immX.getX(); // returns 4
x.setX(2);
immX.getX(); // returns 2, as x still points to the same object as immX
// This would be a compile time error, you cannot call
// non-immutable methods on immutable references to an object:
// immX.setX(1);

This would seem to be fine, however what happens when someone wants to do some caching? Maybe a function call takes a long time, but once the result has been retrieved it won’t ever change. This is the type of edge case where const casting comes into C++, however this allows library users to hack a piece of code they are using and cause problems.
Instead, I propose a ‘mutable’ keyword: when a member variable is marked as mutable, it can be altered by immutable methods. This does of course allow a library writer to store a reference to ‘this’ in a mutable variable, however setting this edge case aside, the user of a library will not be able to mess around with const-correctness without changing the library. The mutable keyword would be used as follows:

class X
{
    mutable double resultCache = -1;
    immutable double longCalculation()
    {
        if (resultCache > 0)
        {
            return resultCache;
        }
        double result = /* some calculation which takes a long time */;
        // store the result in the cache, which is mutable and therefore allowed
        resultCache = result;
        return result;
    }
}

This class could be used as follows:

#X x = new X(); 
x.longCalculation(); // takes a while, first time being run
x.longCalculation(); // returns almost instantly, cached result

Conceptually, this is much nicer than in the alternative in that it allows the programmer more freedom, and immutable objects do not have to be completely pure in order to be treated as immutable.

Script files
Script files basically do exactly what you would expect. Instead of writing:

class X
{
    static void main(String[] args)
    {
        // do some stuff
    }
}

You could write:

// do some stuff

This would just give you exactly the same encapsulating definition as in the first example, but you can leave off all of the cruft before and after the code you want to write.
The only real limitations here are that (a) The command line arguments are already specified as being called args; (b) The class name is already specified as being the basename of the file that the code was defined in; and (c) the script is assumed to be in the default package. Also, it would have a different file extension from normal code files.

Binary Compatibility
This is a very important point. C++, because it is compiled to native code, can give library writers many limitations. These include not being able to add more virtual functions or change the order of virtual functions. These should hopefully be possible to solve by (a) sorting the methods of a class before writing the virtual function table, and (b) adding a ‘since’ construct. The since construct would be a way of marking new methods and member variables; methods with a higher since definition would get added later on in the virtual function table. Here is an example:

class X
{
    void foo() {}
    void foo(int x) { System.out.println("x=" + x); }
    since(1.8.14) void bar(int x) { foo(x*2); }
}

Users who have the old version of X, which only includes foo() and foo(int), will obviously only be able to call those functions. Users who have version 1.8.14 or later will be able to call bar(int) as well, however users who have version 1.8.14 or later and are running software that only assumes they have the old version will still be able to call foo() and foo(int).
This may not seem surprising, but in C++ binary compatibility can be a nightmare because this is impossible. If sorting functions into a better order and playing around with the virtual function table slightly can solve this, then it should be done to make library writers lives much easier.

Native Interface
The native interface would be much simpler than, for example, JNI. Every method that can be called from native code must be marked in the actual code with a “native” keyword and an associated linker name. Similarly, a function that has a native implementation must have the same specifier. An example of a native interface could then be:

class X
{
    native("X_foo") int foo(String s);
    native("X_getRandomNumber") int getRandomNumber() { return 4; }
}

And this could be used in native C code by doing something like:

xlang_int X_foo(xlang_object s)
{
    // a call back up to the String class
    // by the naming convention, this would call s.getChars()
    xlang_char *chars = String_getChars(s);
    printf("%s", chars);
    return X_getRandomNumber();
}

And that is a basic definition of some features I would like to see in a new language. If I come up with anything else I’ll probably update this post. I may try to implement it if I ever get around to it, but don’t expect anything any time soon. Thanks for reading!

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.