Docs of Tart, a future system language: http://docs.tart.googlecode.com/hg/intro/index.html
It looks very similar to D, with few idioms turned into nice syntax. It's in early stage of development. The following are quotations from the docs, I have selected some nice ideas. --------------------------- http://docs.tart.googlecode.com/hg/intro/types.html @Flags enum Traits { ABSTRACT, FINAL, STATIC, MASK = ABSTRACT | FINAL | STATIC, } var x = Traits.ABSTRACT | Traits.FINAL; The presence of the @Flags annotation causes a number of things to happen: The default sequence of initialization values is 1, 2, 4, 8, and so on, rather than 0, 1, 2, 3, etc. In other words, any enumeration constant that does not have an explicit initialization expression will be assigned the next available bit number after the previous value. The compiler will auto-generate the bitwise operators | and & for that type. The compiler will also auto-generate the contains() method, which allows the use of the in operator, enabling expressions such as if ABSTRACT in traits. In the current version of Tart, the toString() method is not defined on flag enums. Note Tart enumerations do not support Javas ability to add arbitrary properties to enums. --------------------------- Property Declarations The def keyword can also be used to define a property. Properties are like variables, except that they are implemented using functions. Whenever you attempt to read from the property, the propertys get function will be invoked, and when you attempt to modify the value, the set function will be called.: def myProp:int { get { return p; } set (value) { p = value; } } myProp = 1; // Calls "myProp.set(1)" --------------------------- http://docs.tart.googlecode.com/hg/intro/functions.html Like Python, parameters can be referred to by name as well as position: print("Hello, World!\n", padding=""); Any parameter can be referred to by its keyword name. The normal mapping of arguments to formal parameters is that positional arguments are assigned first, in order, and then any keyword arguments are assigned to any remaining unfilled parameters. Sometimes it is useful to specify a parameter that is keyword only meaning that it can only be specified via keyword instead of positionally. A semicolon can be used to segregate regular positional parameters from keyword-only parameters: def print (format:String; npos:int=0, sep=false); print("Hello world!", npos=1); // OK print("Hello world!", 1); // ERROR - too many positional arguments In the above example, only the format argument will be filled in by positional argument - to have additional positional arguments in this case would be an error. --------------------------- http://docs.tart.googlecode.com/hg/intro/classes.html A protocol represents a contract which a type may conform to. A class or struct is said to support the protocol if that class or struct defines all of the method signatures that are defined by the protocol. Template arguments can be constrained to only match types which support a specified protocol. Classes may declare explicitly that they support a protocol, or the support can be determined implicitly. A protocol is a kind of abstract type that defines a contract which another type can support. An example would be a HasToString protocol: protocol HasToString { def toString -> String; } --------------------------- The extend keyword allows you to add additional methods to a user-defined type: /* Add an additional method to the String class. */ extend String { static def toUpperCase() { /* ... */ } } Note however, that you cant actually change the runtime representation of a type this way. The reason is simple: The extend declaration may not be visible everywhere in the program. If you extend class String, some modules may only see the original, unextended class, while other modules will see the extended version of the class. In order for all of the code to interoperate, the runtime implementation of the class must be the same, regardless of the extension. This means that the extension can only add certain kinds of things to a type, namely: Static methods or properties. Final methods or properties. Inner types and type aliases. Protocol inheritance declarations. Extensions follow the same scoping rules as other declarations, meaning that they are only in effect if the scope in which they are declared is active. --------------------------- Sometimes you need to test the type of a variable, the isa keyword can be used for this. It works for both reference types and union types: if a isa float { // ... } --------------------------- http://docs.tart.googlecode.com/hg/intro/annotations.html Attributes can take arguments, as in the case of the @Extern attribute, which allows you to declare an external reference to a C-language function. The argument to @Extern allows you to specify the linkage name of the external function: @Extern("create_String") def createString(length:int) -> String; Although the standard library defines many different attribute types, in practice there are only a few that you will encounter on a regular basis: Attribute Meaning EntryPoint Marks the entry point of a program Extern Indicates a definition external to the current module Flags Used to create a flags enumeration Reflect Tells the compiler to emit detailed reflection information. ThreadLocal Indicates a thread-local variable. Different attribute types have different rules for propagating. There are three main modes of propagation: Subtype propagation - attributes from a base class are copied to any subclasses. Member propagation - attributes from a class are copied to the members of the class. Caller propagation - attributes on a function are propagated to the functionss caller. The last type is useful for implementing effect attributes. For example, you could define a @Throws attribute which propagates to any callers: // Any function that calls 'lookup' will also get a @Throws attribute. @Throws(ArrayBoundsException) def lookup(index:int) { return table[index]; } Attributes also have a retention property, which says whether or not the attribute should be retained in the final executable. There are various APIs available for discovering at runtime what attributes are associated with a particular function or variable. --------------------------- http://docs.tart.googlecode.com/hg/intro/reflection.html The @Reflect Attribute The compiler can generate a set of reflection tables that contains all of the information about a classes methods, properties, fields, and so on. However, this information can be quite large, especially when dealing with templates (each unique template instantiation gets its own copy of the reflection tables.) Because of this, the default behavior for the compiler is to only generate minimal information for the class - that is, information about the name, base classes, and type parameters. Information about the methods, properties, fields, and other class members is not generated unless you specifically ask for it. You can tell the compiler to generate the more detailed information by adding the @Reflect attribute to the class: @Reflect class SomeClass { // All class members will be reflected. } This annotation is inheritable, meaning that it applies to all subclasses as well. Because of this, you need to plan ahead when using reflection. --------------------------- http://docs.tart.googlecode.com/hg/intro/collections.html Generator expressions and comprehensions Tart supports generator expressions similar to those found in Python. A generator expression produces an iterator, which can be passed directly to the from() method of a collection: let s0 = ImmutableSet[int].from(x * x for x in 0 .. 10); The initializer-list syntax is also supported, as is the map key/value operator: let s0:ImmutableSet[int] = {x * x for x in 0 .. 10}; let m0:ImmutableMap[int, int] = {x -> x * x for x in 0 .. 10}; Sequence Unpacking Tart supports a Python-like ability to unpack variables from a sequence: let a, b = [1, 2]; let a:int, b:int = [1, 2]; The last variable in the unpacking assignment can be a variadic argument, meaning it scoops up all the remaining values: let a:int, b:int... = [1, 2, 3, 4]; As with function arguments, the ... syntax changes the type of the argument into an array of the explicitly declared type. So the type of b is actually int[], and would in the above example be assigned the value [2, 3, 4]. Variable unpacking works with regular assignment as well, allowing for the Python swap idiom to exchange the values of two variables: a, b = b, a; The sequence-unpacking syntax is what allows you to return multiple values from a function: def returnStringAndInt() -> (String, int) { return "Hello", 12; } let a:String, b:int = returnStringAndInt(); --------------------------- http://docs.tart.googlecode.com/hg/intro/macros.html Tart macros are functions which execute in the compiler rather than at runtime. They are introduced with the macro keyword: macro MyMacro(t:Type) -> Type { // ... } One thing that is interesting about macros is that the arguments are not evaluated before calling the macro. In other words, when you call a macro with an argument of 1 + 2, it does not pass the value 3, but rather the unevaluated expression 1 + 2 (technically what gets passed is an AST fragment.) These expressions will be substituted inline in the body of the macro. This means that you can control how many times (if at all) the expression and its associated side effects are evaluated. There are a number of built-in functions that give you access to the attributes of the AST. For example, stringify() converts an AST node into its string representation. This is used by the assert macro, which is another part of the Tart core library: macro assert[%T](expression:T) { if not expression { throw AssertionError(stringify(expression)); } } --------------------------- http://docs.tart.googlecode.com/hg/intro/templates.html Template guard conditions A guard condition is an additional restriction on the kind of value that can be bound to a template. For example, suppose we wanted a template parameter to only bind to subclasses of Node, so we define a restriction using the subclass test operator <:: class Visitor[%T require %T <: Node] { /* ... */ } Bye, bearophile
