Less punctuationMany C-style languages contain a lot of punctuation. Not only is it easy to get this wrong, it also obscures the core of the code. int myfunc(int a, int b) { int result = 0; for (int i = a; i <= b; ++i) { result += i; } return result; } Zimbu does away with most punctuation: FUNC myfunc(int a, int b) int int result = 0 FOR i IN a TO b result += i } RETURN result } Using } to end a block without a { to start it is a trademark of Zimbu. Perhaps it would be more logical to use END, but it turns out that using } makes the code easier to read. Zimbu encourages using one statement per line. In special cases, e.g., a long list of similar lines, Zimbu allows using a semicolon to separate two statements: CASE 4; a = 3; b = 6 CASE 5; a = 4; b = 8 Type inferenceIn Java you often see a line like this: SomeClass<SomeType> foo = new SomeClass<SomeType>(); Having to specify the type twice is bad. Zimbu has two ways of doing this: SomeClass<SomeType> foo = NEW() VAR foo = SomeClass<SomeType>.NEW() The first way is preferred, declaring the type of var and instantiating an object of that type. The second way is more suited for when the object is created in another way, where you do not need to know the type, e.g.: VAR foo = Foo.createFromBar(bar) When using VAR the type of the variable will be set at the first assignment. All use after that must match that type. To have a variable that can be any type, with runtime type checking, use ANY (this has not been implemented yet).Easy to readPrograms are read many times more often than written. And it must be easy to spot mistakes. For example in large numbers: n = 1'000'000'000 m = 0x12a'f3e7'3b34 x = 0.000'000'01 ImportsWhen using functionality from standard Zimbu libraries there is no need to import anything. The compiler knows where the libraries are. Some languages have the problem that you never know what you get from an import. Especially in C and C++ it is possible to break a working program by adding an item in a header file. In Zimbu an import defines exactly one symbol and it matches the file name: IMPORT "Hello.zu" # Defines Hello and nothing else IMPORT "new/Hello.zu" AS NewHello # Imports Hello as NewHello ClarityIn most Object Oriented languages a class name can be used as a type that can also be a child object of that class. What this actually means is that the variable can store an object that implements the interface of the class. In Zimbu it is possible to really say that a variable can only be an object of a class and not a child class. CLASS Animal # Also defines Animal.I, the interface of Animal ... } CLASS Fish EXTENDS Animal # Automatically implements Animal.I ... Animal a = NEW() # a can only be an instance of Animal. } Fish f = NEW() Animal.I ai = f # ai can be any object that implements Animal.I a = f # Error! A Fish is not an Animal, it's a kind of Animal The .I can be added to any class, thus it is easy to create a class that implements the interface of any other class, without specifically defining an interface. ThreadsStarting a thread to execute a method: PROC sayHello() IO.writeLine("Hello one") TIME.sleepSec(1) IO.writeLine("Hello two") } thread t = NEW(sayHello) t.start() IO.writeLine("Waiting for the thread to finish") t.wait() IO.writeLine("Thread finished") "thread" is the class for controlling a thread that executes a method. The NEW() method takes an argument that is of type "proc<>", a method without an argument that doesn't return anything. More information about the thread class is in the documentation. PipesA pipe can be used to communicate between threads. It takes care of synchronization and locks. Example:VAR pipe = THREAD.eval({ => "hello from the thread!" }) # do something else IO.writeLine("The thread says: " .. pipe.read()) This creates an "evalThread" object to evaluate a lambda function and returns the pipe from which the result can be read. To get access to the evalThread it can be split up into parts: evalThread<string> t = NEW() pipe<string> tp = t.eval({ => "hello thread!" }) Although you can't see it, the Zimbu compiler will check the type of the pipe elements. It sees that the lambda function returns a string, creates "pipe" with the type "pipe<string>". And it knows that "pipe.read()" will then return a string, which is OK for IO.writeLine(). BITS MyFlags bool $done # boolean flag takes 1 bit Kolor $color # enum with 3 values takes 2 bits int5 $count # 5 bit integer } ENUM Kolor red green blue } MyFlags foo = color=red + count=3 IF foo.color == Kolor.red setRed(foo.count) foo.done = TRUE } A BITS value is passed around as a number, but the fields can be accessed by name. Similar to how in C one would use #define a mask for each field to get out the right bits. In Zimbu this is just as efficient plus does type checking and defines the field names local to the BITS instead of the #define that is visible in the whole file. Getters and settersSome argue that direct access to members must not be used, because some day you may want to check the new value or lazily compute the value, and that would require changing all the users of the class. However, the result is a lot of boiler plate code that is added "just in case". With Zimbu getters and setters can be added later without changing the interface of the class: SomeType $member GET() VAR # used when getting $member $updateMember() # lazy updating RETURN $member } GET() OtherType $updateMember() # lazy updating RETURN $member.toOtherType() # type conversion } SET(VAR st) # used when setting $member CHECK.notNull(st) $member = st } SET(OtherType ot) $member = NEW(ot) # type conversion } |