Keeping Things Consistent
The main rule I've tried to follow when designing Oakley is to avoid special cases as far as possible. C# has quite a few of these and they annoy me. For example:
-
Constructors. Why do these have a special syntax? They're just a static function that returns a value. There is no difference in the result of a constructor
new Blah()
and a factory methodBlah.Create()
. However because they have a special syntax they cannot be used in various places where you might use a method; e.g.Func<Blah> function = Blah.Create
is fine but to use a constructor you need to wrap it in a lambda,Func<Blah> function = () => new Blah()
. -
Operators. A similar argument can be made for operators. They're just methods with funny names. So why are they so different in C#? There is a special syntax to remember define them, not all can be defined, and they can't be passed around as
Func
s without wrapping in lambdas first. Other languages such as Scala handle operators better; in Scala you can define pretty much any operator you want and can even call operators with method style syntax, e.g.x + y
could also be written asx.+(y)
. -
Structs. Whilst I can see why they exist they are very much a leaky abstraction. I don't want to care about allocations and garbage and stacks and blah blah blah - that's why I'm using a high level language.
-
Keywords aliases for types. Reinforces the fact that some types are just a bit different to the others.
In Oakley I've tried to be a make things uniform wherever possible. Constructors and operators are methods and can be treated as such. Any operator can be defined, not just some special few. No classes/structs, we just have types and all types are treated the same.
There is one exception to this however, and that is the assignment operator. Assignment is treated as a special case. It wasn't originally; earlier versions allowed you to define it yourself and the parser/compiler code treated it the same as any other operator. However this made some things a little more complicated:
- Translating to C was tricky for assignment - the C function generated had to be slightly different to others and wouldn't work at all for some native types. I therefore had to treat it as a special case and always inline the method.
- The Antlr grammar got a lot more complicated because there are various places where assignment can be used but other operators can't such as assigning an initial value to a field or variable.
I therefore made assignment a special case and made my life a lot easier in the process. It annoys the hell out of me though...