Cobra (programming language)

From Bauman National Library
This page was last modified on 8 June 2016, at 22:01.
Cobra
FileTeplov1.png
Paradigm Multi-paradigm: object-oriented
Designed by Charles Esterbrook
Developer Cobra Language LLC
Typing discipline strong, static, dynamic, inferred
License MIT
Website Template:Url

Cobra is a general-purpose, object-oriented programming language.[1] Cobra is designed by Charles Esterbrook, and runs on the Microsoft .NET and Mono platforms.[2] It is strongly influenced by Python, C#, Eiffel, Objective-C, and other programming languages.[3] It supports both static and dynamic typing.[4][5] It has support for unit tests and contracts.[4] It has lambda expressions, closures, list comprehensions, and generators.[6]

Cobra is an open-source project; it was released under the MIT License on February 29, 2008.[7][8]

Updates are posted to the Cobra news forum with progress on features, fixes, documentation and related projects since the last update.[9]

Features

Object-oriented
Quality control
Expressiveness
  • Static and dynamic binding
  • List, dictionary, and set literals
  • in and implies operator
  • for expressions
  • Slicing
  • Interpolated strings
  • Compile-time type inference
  • Lambdas and closures
General productivity
Scripting conveniences
  • Clean syntax
  • Dynamic binding
  • One-step run
  • Shebang line (#!)
Miscellaneous
  • Documentation tool (cobra -doc)
  • Syntax highlighting tool (cobra -highlight)

Advantages and disadvantages

The unquestionable advantages of language should include a number of built-in features that simplify the development of applications: simple syntax, high productivity, the possibility of static and dynamic input. Developer language Esterbrook said Cobra takes its roots in the "big four languages» - Python, C #, Eiffel, and Objective-C, although a certain influence on him as Visual Basic, D, Boo and Smalltalk. "From the outset, conceived as a successor of Cobra Python and Objective-C", - explains Esterbrook. Already at this stage, the language allows both dynamic and static input poskolku¸ sure how the developer, the choice should remain with the programmers, not the creators of the language imposed; change of approach should not require switching to another language.

The advantages include ease past Esterbrook detect errors in the process of compiling the ability to inform about several errors, as well as high-speed code execution.

Esterbrook compiler called their language self-fulfilling and self-sufficient. But the author admits that Cobra has weaknesses. This he attributes the lack of maturity of the language and the lack of plug-ins for the IDE (integrated development environment, IDE).

Comparing their offspring with Python, Esterbrook notes that the Cobra better implemented error checking, unit testing, math functions, syntax, self-sufficiency and higher performance.

Cobra Types

Primitive Types

  1. Common
    • bool
    • char
      • character literals are single quoted and preceeded by a c.
        • For example var underscore as char = c'_'
    • int (= int32)
    • uint (= uint32)
    • float (= float64)
    • decimal
    • number (defaults to decimal, Can change with compiler -number: option)
    • dynamic (see DynamicType)
  2. Can be explicit wrt Sizes/Sign
    • int8, int16, int32, int64
    • uint8, uint16, uint32, uint64
    • float32, float64
  3. PrimitiveTypeMembers

Complex Types

  1. Class
    • Single inheritance
    • Heap-based
    • Popular classes (provided by platform)
      • Object, String, StringBuilder, Exception
      • List<of T>, Dictionary<of K, V>, Set<of T>
      • Stack<of T>, Queue<of T>
      • TextWriter, TextReader, StringWriter
  2. Struct
    • No inheritance (other than Object)
    • Value-based
    • Popular structs (CLR)
    • DateTime, Color (System.Drawing)
  3. Interface
    • Multiple inheritance
    • No code implementation
    • Popular interfaces (provided by platform libraries)
      • IEnumerable, IEnumerable<of T> (but use T* instead; see streams below)
      • IComparable, IComparable<of T>
  4. Mixins
    • Reusable code implementation ( of interface or abstract class or just some capability)
    • Not instantiated directly
    • class declares use of a mixin explicitly and the implementation is melded into the class implicitly.

Nilable Type

  • Specifies that a variable can be either that type or nil (no-valued, Null)
  • A nilable type is not directly assignable to a non nilable variable of the same Type
    • requires explicit casting to lose or gain nilability ( to !, to ? )
  • Foo? - indicates nilable type Foo - can be type "Foo" or "nil"
  • applicable to all types
  • "dynamic" implies "dynamic?" meaning you can always pass "nil" where "dynamic" is expected

Streams

  • foo* - walkable collection of zero or more objects of type foo
  • See StreamType

Working with types at run-time

  • You can get the type of "x" with "x.getType" (library call) or "x.typeOf" (cobra language extension)
  • You can make instances with a type at run-time
      t = x.typeOf
      obj1 = t()
      obj2 = t(0, 0)

Generics

  • Classes, interfaces and structs can all be generic -- parameterized by type.
  • These are identical to .NET generics as found in C# and VB.
  • Examples: List<of int>, List<of String>, Dictionary<of String, int>
  • The general form is: Name<of T, U...> for a template where T and U are type placeholders.
    • example: class ATag<of T>
    • A generic type is instantiated by using the same name with a concrete type replacing the type placeholders e.g ATag<of String>()
  • You can declare your own and or instantiate generic template types defined locally or in platform libraries.
  • You can overload by number of type arguments. In other words, Foo<of T> and Foo<of T, U> are two different types.
  • Methods can be generic: def foo<of T>(a as T, b as T)

Coding for Quality

Cobra has language-level support for high quality coding via doc strings, unit tests, contracts, nil tracking and assert statements.

Doc Strings

Docstrings can be placed at various specific points in cobracode and are intended as documentation to describe the use of or as a summary of the docced item. Docstrings may be single line or multi line but in both cases are delimited start and end by triple. Docstrings are placed after the item being documented indented as if they were executable code.

Single line docstrings are intended for documenting simple really obvious cases. Multiline are for items that require a bit more description.

For a good description of the form, format and intent of docstrings (wrt python) please see the python PEP.

Docstrings can appear:

  • At the start of a Module (File)
  • ( Should be able to appear immediately after a namespace declaration but thats currently not allowed)
  • Immediately after an Object declaration: class, 'interface, struct, mixin, enum, event
  • Immediately after an extension declaration (extend)
  • after a var or const declaration
  • after a property definition (pro, get, set)
  • after a method or cue (initializer) declaration ( def and cue)
  • after a sig declaration
"""Example module showing docstring positions"""
class AClass
    """ This is an example class containing lots of docstrings."""    var _foofahCount = 0
        """Used only with green wiglets"""
        
    get hasFoofahs as bool
       """True if number of  foofahs > 0"""
       
    pro wigletIsGreen as bool
        """Triggers foofah count if true."""    cue init
        """Std Initializer."""
        base.init
        
    def doNothing
        """Nothing happens in this method. comeback later"""
        pass

Tests

Support for provision of (unit) Test code is built into the cobra language.

Tests are specified as clauses on Class members (methods and properties) or on Class types(?).

They are given as an indented block of standalone code prefixed by the test keyword suffix with an optional test name. The indented code block does the setup for a test (object instantiation and state initialisation), executes the test case (or cases), verifies ( usually by assertions) that the test passes followed by any necessary teardown.

Tests are executed whenever the program or code is run (unless tests are suppressed at build by the -include-tests:no compiler option).

Failures show up as an assert failure and a stack trace.

Example

class Incrementer

    var counter =0
    var val = 0

    test
      incr = Incrementer()
      assert incr.counter == 0
      assert incr.val == 0 
      i = incr.bump(2)
      assert i == 2
      assert incr.counter == 1
      assert incr.val == 2
      i = incr.bump(3)
      assert i == 5
      assert incr.counter == 2
      assert incr.val == 5

    def bump(inc as int) as int
        test
          incr = Incrementer()
          incr.bump(1)
          assert incr.counter == 1
          assert incr.val == 1
          incr.bump(2)
          assert incr.counter == 2
          assert incr.val == 3
        body
           .counter += 1
           .val += inc
           return .val

Contracts

Cobra supports contracts which are expressed through lists of assertion expressions (must-evaluate-true) attached to objects and methods (and properties). They act as documentation, specification and run-time assertions and should be viewed as part of the public interface of the items they are associated with.

When a contract condition fails, an exception is raised. The exception message shows the value of each subexpression in the condition, just like the Cobra 'assert' exception. For example, 'x > y' failing will show the values of 'x' and 'y':

Unhandled Exception: Cobra.Lang.RequireException: 
sourceSite = demo.cobra:5 in Demo.subtract for object Demo
info       = nil
this       = Demo
    (x > y) = false
        x = 3
        y = 6

Compile-time Nil Tracking

Cobra calls it nil as do Smalltalk, Objective-C and LISP. Other languages call it NULL, Nothing, None and null. It's customary in most languages to be able to pass nil anywhere, or at least where instances of classes are expected. But in Cobra, nil can only be passed when allowed by a nilable type: one suffixed by a question mark (?).

class Foo
    def bar(s as String?)
        if s # same as "if s is not nil"
            print Utils.countChars(s, c'x')

All Cobra Statements

All statements in alphabetical order:

Assert

The assert statement provides a way to verify the internal state of your code. To the assertion, you provide a conditional expression that is expected to evaluate true. If the verification fails (condition is false), an exception (AssertException) is raised.

The exception reports the source code of the condition, the file name, the line number, the current object and information provided as an optional second argument to the assert. However, it's rarely necessary to provide a second, informational argument because a failed assertion also reports the value of every subexpression of the condition.

For example assert obj.foo < bar failing its assertion will report the value of obj.foo, obj and bar. This makes assertions quick to write and easy to diagnose.

Example

assert i > 0          
assert obj.foo < bar  
assert not badValue   # test that a boolean value is false
assert obj            # assert obj not null
assert name.length    # assert name is not 0 length

Branch

The branch statement is used to conditionally execute code where there are a range of execution options on a single variable value.

Example

 x = 2 
    branch x 
        on 1 
            isEven = false 
            word = 'one' 
        on 2 
            isEven = true
            word = 'two' 
        on 3 
            isEven = false 
            word = 'three' 
        else
            word='UNKNOWN

Break

The break statement terminates the execution of the nearest enclosing loop in which it appears. Control passes to the statement that follows the terminated statement, if any.

Example

def main is shared
    for i in 1 : 10
        print i

        if i == 4
            break

Continue

The continue statement forces transfer of control to the controlling expression of the nearest (smallest) enclosing loop. Any remaining statements in the current iteration are not executed. The next iteration of the loop is determined as follows:

  • In a while loop, the next iteration starts by reevaluating the controlling expression of the while statement.
  • In a for loop, continue causes the loop counter to increment or enumerator step to be executed then the loop termination cond-expr is reevaluated and, depending on the result, the loop either terminates or another iteration occurs.

Example

def main is shared
    i = 0
    post while i < 3    # do
        i += 1
        print 'before the continue'
        continue
        print 'after the continue, should never print'

     print 'after the do loop'

# Outputs:

before the continue
before the continue
before the continue
after the do loop

Expect

This is a statement that provides an assertion capability for checking for thrown exceptions. Its usually used in unit test code.

Example

expect FormatException
    i = Int.parse('abcdefg')

expect AssertException
    assert 0 > 1

ints = [1,2,3] 
expect ArgumentOutOfRangeException, print ints[5]

expect MyInvalidInputException
    checkInput(GenericInvalidInputObject)

Lock

Specify a code block to execute after obtaining a lock on the object of an expression. The code block constitutes a critical section that only one thread may execute at a time. This construct ensures that one thread does not enter a critical section of code while another thread is in the critical section gated by the same locked object. If another thread attempts to enter a locked block, it will wait, blocking until the lock on the object is released.

Example

# Critical section is to generate output through a single shared writer fm multiple threads
class Tester
    var _statusWriter as Writer?
...
    cue init
    ...
        _statusWriter = MyWriter()
        # gen and setup multiple threads with .runInThread as the thread code to run
    ...    def runInThread
        while true
            ...
            lock _statusWriter
                _statusWriter.writeLine(statusLine)

For-numeric

If <start> is not specified it defaults to 0. If <step> is not specified it defaults to 1. For each iteration <variable> gets a value beginning at <start> incrementing by <step> and ending when the <stop> value is reached or exceeded ( or a break statement is encountered). Inside the statements, a break statement will exit the loop and a continue statement will skip the remaining statements. In either case, an if statement is used to control when the loop is broken or continued (otherwise it would be pointless to have further statements in the loop).

Example

  for i in 1:10
     print i   # prints values 1,2,3,4,5,6,7,8,9

  for i in 1 : 10  # can have spaces around :
     print i

  for i in 5
    print i    # prints values 0,1,2,3,4

  for i in 7:5:-1
    print i    # prints values 7,6

  for e in 20:31:2
    print i    # prints even numbers between 20 and 30 inclusive

  for f in 7:5
    print f    # never executed

  for i in someList.count
    print i    # print the list indexes

For-Enumeration

The enumerable for statement is used to enumerate the elements of an object—any object that implements IEnumerable.

Example

# Example 1
for person in people
    print person

# Example 2
i = 1
for person in people
    print '[i]. [person]'
    i += 1

# Example 3
for line as String in lines
    line = line.trim
    if line
        print line

# Example 4
for node in nodes
    if not node.isActive
        print 'skipping inactive node: [node]'
        continue
    print node
    node.process

# Example 5
found = false
for cust in customers
    if cust.name == name
        found = true
        break

If-Then-Else

The if statement is used to conditionally execute code.

Example

# Example 1
if name
    print 'Hello, [name].'

# Example 2
if name
    print 'Hello, [name].'
else
    print "I don't know your name."

# Example 3
if x < y
    print 'x is smaller'
else if x > y
    print 'x is larger'
else
    print 'x and y are the same'

Ignore

Disconnect an event from some particular event handling code by specifying an existing event handler (method reference) for a particular Event instance.

Example

use System.Windows.Forms

...
        button = Button(parent=p, autoSize=true, text='One', tag=1)
        # connect event and handling code
        listen button.click, ref .clickHandler
        ...
        # disconnect event and its handling code
        ignore button.click, ref .clickHandler
...
def clickHandler(source as Object, args as EventArgs)  # doClick
    pass

Listen

Connect an event to some particular handling code by giving an event handler (method reference) for a particular Event instance. When the event is raised (or fired) the method (event handler) is invoked with the args given to raise the event.

An event is specified as a name typed to a sig (signature/delegate). Any method given as an eventhandler/listener must conform to the same sig.

Example

use System.Windows.Forms

...
        button = Button(parent=p, autoSize=true, text='One', tag=1)
        listen button.click, ref .clickHandler
...
def clickHandler(source as Object, args as EventArgs)
    pass

Pass

Do nothing successfully.

Used as a placeholder or for null operation where code is expected (like in a block) but nothing needs to be done.

Example

def methodNeededForInterface # but not used
    pass

# simplify complicated code flow.
if .complicatedExpression0
    if .expression1 and .expression2 and not .expression3 or expression4 
        pass # all is well
    else if expression1 and not expression2
        .expressFailure
    else if .expression3
        .expressFailure2
    # ...

# property with both getter and setter, setter must be available but does nothing
pro isUsed as bool
    get
    return false
    set
    pass

try
   .doOperation
catch FormatException
   pass    # trap and discard the exception

Post(while)

Repeatedly execute code while a condition is true. Evaluate the condition either before executing the code ( the first time) or afterwards.

Example

# Example 1
while obj.someCondition
    obj.process

# Example 2
while node
    print node
    node = node.next

# Example 3
while node
    if not node.isActive
        print 'skipping inactive node: [node]'
        continue
    print node
    node.process
    node = node.next

# Example 4
while true
    input = Console.readLine
    if input is nil or input.trim.toLower == 'exit'
        break
    .processInput(input)

# Example 5
post while line and line.trim == ''
    line = stream.readLine

Print

Print the given expression or expressionlist. e.g.

Example

print   # emit a blank line
print 'Hello World'

Raise

Raises an event which will directly notify any listeners for that event. An event can only be raised for the same class and the same instance.

The number and types of args given in the raise statement must match those declared in the sig used as the type for the event being raised with two exceptions:

  1. The sender which is always this should not be specified.
  2. The event arguments object can be left out if you are happy with a default instantiation.

This will become more clear in the examples below.

Example

use System.Windows.Forms

...
        button = Button(parent=p, autoSize=true, text='One', tag=1)
        # connect event and handling code
        listen button.click, ref .clickHandler
        ...
        # trigger the button programatically
        raise button.click, EventArgs()
        ...
        # disconnect event and its handling code
        ignore button.click, ref .clickHandler
...
def clickHandler(source as Object, args as EventArgs)
    pass

Return

The return statement terminates execution of the method in which it appears and returns control to the calling method.

The value of the expression clause is returned to the calling function converted to the type specified in the method declaration. Constructors and destructors, and functions of type void, cannot specify an expression in the return statement. Functions of all other types must specify an expression in the return statement

If the return statement is inside a try block, the finally block, if one exists, will be executed before control returns to the calling method.

Example

#Use an expression with a return statement to obtain the largest of two integers.
def max(a as int, b as int) as int is shared
    return if(a > b, a, b)

def main is shared
    nOne = 5
    nTwo = 7

    print String.format( '{0} is bigger', .max( nOne, nTwo ) )

Throw

The throw statement is used to signal the occurrence of an anomalous situation (exception) during the program execution.

Example

class MyException inherits System.Exception
    pass
# ...
throw MyException()

Trace

Display an execution trace for

  • a point of execution or
  • an expression or expressionlist or
  • all args and locals in the current method

or enable or disable subsequent trace output.

Tracing an expression or expressionlist is unlike a print of the same expression in that

  • The display format is different; trace generates expression-text = expression-value for each expression
  • Position information (filename, line number, declaring class and method) of the trace line is printed
  • Traces can be suppressed on a build by a cobra commandline argument

cobra -include-traces:no ...

traces are useful for debugging (displaying interim expression values) and logging a point of execution( e.g. indicating that code in a code block is being executed).

A simple trace statement with no arguments gives just the position info of the trace statement - the filename, line number, declaring class name and declaring method name that the trace is located in. If the current object's class is a subclass of the declaring class then the subclass name is reported as well.

When given one or more expressions, trace gives the same information plus the source code and value of each expression.

The trace all statement is a convenience for logging

  • this,
  • every method argument and
  • every local variable.

The trace off statement turns off the subsequent trace statements in the declaring method.

The trace on statement turns them back on.

Example

class Foo 

    var _z as int 

    def computeStuff(x as int, y as int) 
        if x > y 
            trace 
            return 
        _z = x * y 
        trace all 
        trace _z

trace: at Foo.cobra:7; in Foo.computeStuff
trace: this=Foo; x=4; y=2; at Foo.cobra:10; in Foo.computeStuff
trace: this=Foo; _z=8; at Foo.cobra:11; in Foo.computeStuff

Try-Catch

A statement that specifies to run (try) a block of code and to trap (catch) and handle any exceptions thrown from it.

Exceptions can be specified to be trapped in one of three ways

  • catch exception type and capture the exception instance for use in its corresponding catch block.
  • catch exception type ignoring actual instance
  • catchAll clause - catch any exceptions not already given

There can be multiple catch clauses given specifying different exceptions to trap but there can be only one 'catchAll' clause.

The success clause is optional and if specified is executed only if no catch clauses are invoked (i.e if no catch specified exceptions are thrown).

The finally clause is also optional and if specified is invoked after all other clauses regardless of whether any exceptions were thrown, trapped and/or rethrown. Control is always passed to any finally block regardless of how the try block exits.

Whereas catch clauses are used to handle exceptions that occur in a statement block, finally is used to guarantee a statement block of code executes regardless of how the preceding try block is exited. Used for must-cleanup situations.

Example

# parsing numbers
s = tok.text.replace('_', '')
if s.endsWith('f')
    s = s[:-1]
try
    value = float.parse(s, Utils.cultureInfoForNumbers)
catch FormatException
    .recordError('Unexpected FormatException for float literal "[tok.text]".')
catch OverflowException
    .recordError('Range overflow for float literal "[tok.text]".')

# reading file contents
contents = 'test: failure'
try
    contents = File.readAllText('myFile.txt')
catch ioe as IOException
    print 'I/O Error: [ioe.message]'
success
    assert 'test' in contents# customException
try
    if source.length == 0
        _warning('File is empty.')
    else if source.trim.length == 0
    _warning('File is completely blank.')
    return _parseTokens()
catch pe as ParserException
    if _errorRecorder, _errorRecorder.recordError(pe)
    _tokenizer = nil
    throw  # rethrow exception trapped above

Using

Allows IDisposable object acquisition and defines a scope outside of which the the object will be disposed in the correct manner. This provides a convenient structure that encapsulates the correct use and release of an IDisposable object.

The idiom is that an IDisposable object is acquired/constructed in the using statement expression, assigned to a local variable and used within the block. When the block terminates (for any reason, fall through, exception or return) the using statement calls the .dispose method on the object in the correct way, and also causes the object itself to go out of scope as soon as .dispose is called. Within the using block, the object is read-only and cannot be modified or reassigned.

Пример

using font1 = new Font("Arial", 10.0f) 
    charset = font1.GdiCharSet
    # do domething with charset
# font1 disposed of (and out of scope) here

Use

Specify a namespace and its contents to make available to this module.

Example

use Foo.Bar from "My Lib"

While

Repeatedly execute code while a condition is true. Evaluate the condition either before executing the code ( the first time) or afterwards.

Example

# Example 1
while obj.someCondition
    obj.process

# Example 2
while node
    print node
    node = node.next

# Example 3
while node
    if not node.isActive
        print 'skipping inactive node: [node]'
        continue
    print node
    node.process
    node = node.next

# Example 4
while true
    input = Console.readLine
    if input is nil or input.trim.toLower == 'exit'
        break
    .processInput(input)

# Example 5
post while line and line.trim == ''
    line = stream.readLine

Yield

The yield keyword signals to the compiler that the method in which it appears is an iterator block. A new class is created and compiled to implement the behavior that is expressed in the iterator block (implementing an IEnumerable).

Within the iterator block, the yield keyword is used to provide a value to the enumerator object. This is the value that is returned, for example, in each loop of a for statement.

In a yield statement, the expression is evaluated and returned as a value to the enumerator object; expression has to be implicitly convertible to the yield type of the iterator.

In a yield break statement, control is unconditionally returned to the caller of the iterator and signals the end of iteration.

The yield statement can only appear inside an iterator block, which can be implemented as the body of a method, operator, or accessor.

  • A yield statement cannot be located anywhere inside a try-catch block. It can be located in a try block if the try block is followed by a finally block.
  • A yield break statement may be located in a try block or a catch block but not a finally block.

Example

use System.Collections

class Pwr
    def power(num as int, exponent as int) as IEnumerable is shared
        counter = 0
        result  = 1
        while counter < exponent
            counter += 1
            result = result * num
            yield result
       yield break    

    def main is shared
        # Display powers of 2 up to the exponent 8:
        for i in .power(2, 8)
            print i
/#
Output:
2 4 8 16 32 64 128 256 
#/

The influence of other languages on the Cobra

Cobra was created under the influence of a variety of programming languages:

  • Python, Ruby — syntax
  • C#, C++ — productivity
  • Objective-C, Visual Basic — static and dynamic types
  • Eiffel, Spec# — contract programming method
  • Spec#, iihtdioa, C# — Compile-time Nil Tracking

IDE

  • Visual Studio
  • MonoDevelop

References

  1. "The Cobra Programming Language". Cobra Language LLC. Retrieved 2012-09-26.
  2. Charles Esterbrook (Jan 28, 2008).Lang.NET Symposium 2008 – The Cobra Programming Language. Microsoft. Retrieved 2010-08-31.
  3. Bridgwater, Adrian (5 March 2008)."Cobra takes a bite at open source". ZDNet UK. Retrieved 2010-08-31.
  4. 4.0 4.1 Neward, Ted (June 2009). "Reaping the Benefits of Cobra". MSDN Magazine.
  5. Erickson, Jonathan (April 2008). "Was George Costanza a Computer Programmer?". Dr. Dobb's Journal.
  6. Morris, Richard (April 2010)."Chuck Esterbrook: Geek of the Week". simple-talk.
  7. Krill, Paul (Feb 7, 2008). "Cobra language slithering to open source". InfoWorld. Retrieved 2010-08-31.
  8. "The Cobra Programming Language". Cobra Language LLC. Retrieved 2008-02-29.
  9. Cobra News Forum

External links