Summary
Cobra 0.5 adds dynamic typing, the trace statement, better error checking and fixes several bugs.
Additions / Major Improvements
- Cobra now supports dynamic typing in addition to static typing. Now you, the programmer, can decide which to use and even in what proportion. Here are some examples:
class Person
get name as String
return 'Blaise'
class Car
get name as String
return 'Saleen S7'
class Program
shared
def main
assert .add(2, 3) == 5
assert .add('Hi ', 'there.') == 'Hi there.'
.printName(Person())
.printName(Car())
def add(a, b) as dynamic
return a + b
def printName(x)
print x.name # dynamic binding
In general, all decisions about dynamic values are deferred until run-time where they will either succeed or throw an exception. Some operations, such as assigning a value to a dynamic variable, will
never throw an exception. Some statements such as print, trace, if and while work with any kind of object/value
and therefore never complain at run-time about the actual value of a dynamically typed expression.
In code, you can refer to the type as dynamic, but there are three places where you need not bother because absence of typing implies dynamic:
- class variables (also called "fields" in .NET)
- method and indexer arguments
- property types
Dynamic typing offers more flexibility, but with costs including decreased performance. This is summarized here:
def add(a, b) as dynamic
return a + b
# + flexible (any type with "+" operator works)
# + fast prototyping
# + less brittle wrt other software components that change unpredictably
# + more reusable
# - errors detected late (run-time)
# - one error reported at a time (the first one that throws an exception)
# - slow at run-time
# - fat at run-time (values must carry type information; boxing)
# - difficult IDE support
# (Intellisense/autosuggestion requires complex analysis and/or execution of code)
def add(a as decimal, b as decimal) as decimal
return a + b
# - inflexible
# - slower coding / more typing
# - more brittle (to change program to `float` you must find and replace everywhere)
# - less reusable (cannot use with int and float)
# + errors detected early (compile-time)
# + multiple errors reported (every one that the compiler can find)
# + fast at run-time
# + slim at run-time (values need only carry data)
# + easy IDE support (Intellisense/autosuggestion)
Note that "nilable" trumps dynamic: You cannot assign nil to a variable or property of type dynamic, but you can do
so to one of type dynamic?.
Note that there are some changes with the introduction of dynamic. The types for empty collection literals now use dynamic:
t = [] # t is a List<of dynamic> now, not a List<of Object>
d = {} # d is a Dictionary<of dynamic, dynamic>
Also, the type returned from calling a System.Type is now dynamic instead of Object:
def makeNamedObject(Type type) as dynamic
obj = type() # obj is dynamic instead of Object now
trace obj.name # `name` is looked up at runtime since `obj` is dynamically typed
return obj
# You can typecast if you know the base type involved:
def makeNamedObject(Type type) as INamedObject
obj = type() to INamedObject
trace obj.name
return obj
- The new trace statement offers a better alternative to using print statements for debugging. Advantages include printing the source of the statement
(file name, line number, class name, etc.), auto-flushing, turning traces on/off, etc. See the trace statement documentation
for details.
- There is a new Reference Manual which, although incomplete, is useful to people new to Cobra.
Other Enhancements
- The Cobra compiler is about 17% faster. This speed improvement is more easily seen on larger code bases than smaller ones (perhaps because of the overhead of disk I/O in either case).
- Added runtime property CobraCore.exePath which is more convenient than what it covers: System.Reflection.Assembly.getExecutingAssembly.location
- Added runtime methods CobraCore.findCobraExe and CobraCore.findCobraExe(extraPaths as IList<of String>?). These
are convenient if you want to invoke the Cobra command line from your program.
Compile-time Error Checking
- Better error message for when people try to use Python or C# syntax for class (or interface) inheritance:
The syntax for inheritance is to put "inherits BaseClass" on the following line, indented.
- Better error message for C-style argument declaration or local variable declaration:
def
foo(String s)
# error: The correct parameter syntax is "paramName as ParamType". Try "s as String".
...
int i = 0
# error: The correct local variable syntax is "name as Type" or "name = initValue". Try "i as int".
- New error check in case Pythonistas forget to say "throw" instead of "raise":
raise
Exception('message')
# error: The "raise" keyword may be used in the future to raise events.
# If you are trying to throw an exception, use the "throw" keyword.
Generic parameter names must start with an uppercase letter in order to
avoid collisions with other identifiers such as arguments and local variables.
Fixes
- Made the in operator work with non-generic lists, non-generic dictionaries and arrays.
- Slicing now works on arrays.
- Added support
for unary plus (doh!): obj.foo(+1)
- "cobra -test foo.cobra" now works for programs. Previously, it only worked for libraries.
- "cobra -test" now
tests classes that are contained in namespaces. Previously, it would silently skip them.
- Eliminated false error message when invoking base.init when the base
class' initializer was overloaded.
- An expression statically typed as Object that contains a boxed decimal value of 0.0 now yields false
as it should (rather than true).
- Mathematical operators can now be used when the right hand side of an expression is nilable.
- Fixed bugs with indexing qualified types
such as t[i] where t is a System.Collections.IList.
- Fixed bug where string literals with substitution did not show up in assert expression
break downs.
- Fixed a bug with the /= operator.
- Fixed a bug with the != operator (that's inverse nil coalescence assignment, not does not equal (<>)).