Simplicity is the main goal

The language is simple for several reasons:
  • prototype-based, prototypes play the role of classes, used for the creation of new objects. But prototypes are also objects. Just one concept instead of two. There is no need of classes of classes for considering classes as objects. This fact alone reduces enormously the size of the language. Example:
    	
    
    object Program func run { var Int n; // Int can be used in expression, it is 0 n = Int*Int + Int; assert n == 0; } end
  • methods by default are public and fields are always private
  • single inheritance with interfaces
    object Student extends Person implements IDriver end
  • prototype Any is the superprototype of every other prototype but Nil. Hence, basic types like Int are subtypes of Any
    var Any any; any = 0; any = "abc";
  • there are very simple rules for type checking, nothing unusual is allowed
  • Smalltalk-like syntax for declaring and sending messages. Example:
    object Program func run { // message passing, the receiver is Out Out print: "factorial of 5 = "; // the receiver is 'self' in 'self fat: 5' Out println: (self fat: 5) } func fat: Int n -> Int { if n == 1 { return 1 } else { return n*(fat: n-1) } } end
  • methods can have operator names such as + and * but the prototype should be immutable
    object Complex func + (Complex other) -> Complex { return Complex(re + other getRe, img + other getImg); } ... end
  • the language supports several kinds of literals: arrays, tuples, intervals, and maps. Example:
    object Program func run { // literal String array of type Array var months = [ "jan", "fev", "mar" ]; // a literal tuple var t = [. "one", 1 .]; // 't f1' is an unary message passing // it returns "one" Out println: (t f1) ++ " = " ++ (t f2); // a literal named tuple var nt = [. name = "one", value = 1 .]; Out println: (nt name) ++ " = " ++ (nt value); // 1..10 is an interval for n in 1..10 { n println } // a literal map var nameValueMap = [ "one" -> 1, "two" -> 2 ]; // ... } end
  • anonymous functions make it easy to code.
    var sum = 0; var f = { (: Int n :) sum = sum + n*n }; // 'eval:' calls the function f eval: 5; // the same as sum = sum + 25; assert sum == 25; sum = 0; // sums the squares of numbers from 1 to 10 1..10 foreach: { (: Int n :) sum = sum + n*n }; sum println;
    The function is given between { and } with parameters between (: and :). In the above code, the function is the argument to method foreach:
  • constructors are always called init or init:
    object Box func init { value = 0; } func init: Int value { self.value = value } ... var Int value end
  • less need of parentheses for programming
  • downcast, the conversion of an object of a superprototype to an object of a subprototype should be made using the type-case or cast statements. Example:
    var Any any = 0; cast Int n = any { // only executed if 'any' can be // converted into an Int var sqr = n*n; sqr println }
    Example with type-case:
    var Any any = "abc"; type any case Int n { (n*n) println } case String s { assert s[0] == 'a' && s[1] == 'b'; }
  • prototype fields are only initialized in their declarations or in the constructors
  • the code has fewer parentheses than in most object-oriented languages because a) { and } should always be given in statements if and while and b) message passing does not demand ( and )
  • space is demanded before and after = and some operators as the relational (<, >=, etc) operators
    var Int n; // ok n = 0; // compile-time error n=0; if n< 0 { // compile-time error ... }
  • no checked exception handling system
  • arrays are regular prototypes declared in package cyan.lang imported by every Cyan source code
    var Array<Int> ai; ai = [ 0, 5, 9 ]; ai[2] println;
  • types need not be supplied in several situations as local variable initialization
    // the compiler deduces that n is Int var n = 0;
  • method arguments are always evaluated from left to right
  • specific binary operator ++ for converting both sides to String and concatenating them
    assert "0" ++ 1 == 0 ++ "1"; assert 0 ++ 1 == "01";
  • non-shared fields and methods can only be called using the prototype in which they are declared, as in MyList maxSize, or inside the prototype without an explicit receiver (as maxSize)
  • prototypes with operator methods should be immutable. That helps preventing operators being used for abstractions that are not related to mathematics
  • prototypes declaring methods with ‘package’ visibility can only be inherited by prototypes of the same package. That prevents some confusing code in which a method defined in prototype A is not visible in B, subprototype of C, and again visible in a prototype C that inherits from B. See article ‘Traps in Java’
The language does not support:
  • nested prototypes and methods
  • overloaded methods unless they are clearly marked with keyword overload
  • default parameters in methods
  • complex rules in generic prototypes
  • the return statement inside anonymous functions and, therefore, inside try blocks used for implementing exception handling
  • implicit conversion between basic types. Value 1 is not automatically cast to 1.0, for example. All conversions should be made by message passing as in n asDouble for converting an Int variable n into a Double
  • default values for prototype fields, all of them should be explicitly initialized
  • extension methods
  • postfixed unary operators ++ and --. The code ++n is a statement equivalent to n = n + 1