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 method Blah.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 Funcs 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 as x.+(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...