Cobra (programming language)
This page was last modified on 8 June 2016, at 22:01.
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]
Contents
Features
- Object-oriented
-
- Namespaces
- Classes, interfaces, structs, extensions, enumerations
- Methods, properties, indexers
- Mixins, extension methods
- Generics, attributes
- Quality control
-
- Contracts, assertions
- Unit tests, docstrings
- Compile-time nil-tracking
- Expressiveness
-
- Static and dynamic binding
- List, dictionary, and set literals
-
in
andimplies
operator -
for
expressions - Slicing
- Interpolated strings
- Compile-time type inference
- Lambdas and closures
- General productivity
-
- Exception handling
- Postmortem exception report
- Garbage collection
- Miscellaneous
-
- Documentation tool (
cobra -doc
) - Syntax highlighting tool (
cobra -highlight
)
- Documentation tool (
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
- Common
- bool
- char
- character literals are single quoted and preceeded by a c.
- For example var underscore as char = c'_'
- character literals are single quoted and preceeded by a c.
- int (= int32)
- uint (= uint32)
- float (= float64)
- decimal
- number (defaults to decimal, Can change with compiler -number: option)
- dynamic (see DynamicType)
- Can be explicit wrt Sizes/Sign
- int8, int16, int32, int64
- uint8, uint16, uint32, uint64
- float32, float64
- PrimitiveTypeMembers
Complex Types
- 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
- Struct
- No inheritance (other than Object)
- Value-based
- Popular structs (CLR)
- DateTime, Color (System.Drawing)
- 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>
- 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 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:
- The sender which is always this should not be specified.
- 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
- ↑ "The Cobra Programming Language". Cobra Language LLC. Retrieved 2012-09-26.
- ↑ Charles Esterbrook (Jan 28, 2008).Lang.NET Symposium 2008 – The Cobra Programming Language. Microsoft. Retrieved 2010-08-31.
- ↑ Bridgwater, Adrian (5 March 2008)."Cobra takes a bite at open source". ZDNet UK. Retrieved 2010-08-31.
- ↑ 4.0 4.1 Neward, Ted (June 2009). "Reaping the Benefits of Cobra". MSDN Magazine.
- ↑ Erickson, Jonathan (April 2008). "Was George Costanza a Computer Programmer?". Dr. Dobb's Journal.
- ↑ Morris, Richard (April 2010)."Chuck Esterbrook: Geek of the Week". simple-talk.
- ↑ Krill, Paul (Feb 7, 2008). "Cobra language slithering to open source". InfoWorld. Retrieved 2010-08-31.
- ↑ "The Cobra Programming Language". Cobra Language LLC. Retrieved 2008-02-29.
- ↑ Cobra News Forum
External links
- Official website
- The Cobra blog by Charles Esterbrook
- Cobra News Index
Присоединяйся к команде
ISSN:
Следуй за Полисом
Оставайся в курсе последних событий
License
Except as otherwise noted, the content of this page is licensed under the Creative Commons Creative Commons «Attribution-NonCommercial-NoDerivatives» 4.0 License, and code samples are licensed under the Apache 2.0 License. See Terms of Use for details.