Cobra 0.4 adds arrays, supports space-based indentation, corrects contract semantics, makes runtime failures easier to analyze, refines the language and fixes bugs.
Additions / Major Improvements
- Cobra now supports arrays. Their absence hasn't been much of a problem given the existence of generic lists except that much of the .NET/Mono standard library expects arrays for parameters (as opposed to other meaningful choices such as IEnumerable<of T> or IList<of T>).
- The definition of truthfulness has been changed to be anything that is not:
Note that blank strings and empty collections are now considered to be true because they are non-nil (previously they were false for having a length or count of zero).
This eliminates most is not nil and <> nil from the code, while adding .length and .count when dealing with strings and collections. In the source code of the Cobra compiler itself, the is not nil cases outnumbered the "truthfulness of an object" case by 2 to 1. So there is a net reduction in code. And for all the times one might have forgotten to say is not nil when only non-nilness was required, Cobra programs are now more efficient. There is a more thorough blog entry on the topic.
- You can use spaces in place of tabs for indentation. Four spaces equal one level of indentation. Mixing spaces and tabs for indentation in the same line is still an error.
Contracts are a major feature of Cobra (largely inspired by and copied from Eiffel). Previous versions of Cobra had some incorrect semantics and deficient syntax regarding contracts. These are now corrected by the following improvements:
Requirements in the inheritance chain are "OR"ed at runtime. This means that a subclass can override a method and provide an alternative requirement for execution of the subclass method. However, a call to base.methodName will still require the original contract (although no base call is ever required).
In that context, RequireException has been given a next property that chains them together.
Note that a missing require for the top level method (or property) is like require true. But an overriding method with no require will inherit the require of its base method.
Ensurements in the inheritance chain are "AND"ed at runtime. This means that a subclass can override a method (or property) and add additional ensurements. It also means that the overriding method must still conform to the ensurements of the base class whether it invokes it or not.
To highlight the semantics of contract inheritance, the syntax in an overriding method is now or require (rather than only require) and and ensure (rather than only ensure).
In summary, overriding methods can strengthen requirements and weaken ensurements.
And now an example:
- The RequireException and EnsureException now include this in their info which makes failure analysis easier.
- Requirements now work with shared methods. And when the base class has no require. (bugfixes)
Refinements / Minor Improvements
- Assert, require and ensure now give an expression breakdown. This practically eliminates the need to put extra information with an assert. For example, a failure of "assert obj.foo < bar" will report the value of obj.foo, obj and bar. This nifty feature makes assertions quicker to write and easier to diagnose.
- print a, b now puts a space between a and b.
- Added CobraCore.typeName(t as Type) which returns a Cobra-centric name for the given type. For example, "int" instead of "Int32" or "List<of int, String>" instead of "List1".
- Enhanced CobraCore.toTechString(x) to display the contents of dictionaries, and to prefix lists and dictionaries with their typenames (using CobraCore.typeName() of course).
- FallThroughException now reports the CobraCore.toTechString() of its info argument which can yield more information (especially for collections).
- Added an error message for "x is not nil" if x's type is not nilable in the first place:
You will not get this error if x's type is System.Object because that may refer to boxed primitives such as bool and int, which will be treated correctly at run-time.
- Keywords can now be used for method and property names. Since these are always accessed with a dot (.), there isn't even any need for special quoting.
- Added these CobraCore properties to control checking at runtime:
- Eliminated the use of ? and ! as suffix operators on expressions. These were just shortcuts for is nil and is not nil. The ? suffix operator created an ambiguity with types and now casting expressions like x to String?, which previously failed, now work as they should.
- As with methods, Cobra now gives an error for properties and indexers that are missing "is override", "is new" or the invocation of "base" when needed.
- The short form of a read/write property (pro foo from var) did not pick up on shared when declared inside a shared block.
- The nil coalesce expression (x ? y) would not parse when x was something more complex than a single identifier or literal.
- Casting to a nilable class such as x to String? now works.
- Cobra now recognizes base.foo as implying is override when base.foo is found, not in the parent (a case that worked), but further up the inheritance chain.
- Non-nilness is now enforced on class variables at run-time, as originally intended. In other words, when you write:
The variable _name cannot be nil at the end of init. You must set it to a non-nil value or change the type to String?. Otherwise, an assertion will fail at run-time.
- "If inherits", whether for typing or non-nil purposes, now works on class variables (instead of only on parameters and locals).
- The error message "Cannot find X in Y." for "Y.X" was showing debugging information for Y rather than its name (such as the name of a namespace).
- Enums can now be declared in namespaces.
- HowTo\350-MakeACollectionClass now works.