Here some of the features of Mantra:
- First of all, Mantra has an awesome name!
The file extension is ".om" as in "auuuum", the chant. I love that.
- Dynamically typed with type annotations.
int add(int x, int y) { return x+y; }
The type annotations make you think that mantra is statically typed but it is not. There are no typecasts and you are free to say x.close() where x is an integer if you want. The types buy you readability and Mantra can generate more efficient code for you. Mantra can also generate pre-and post-condition checks on the types of the incoming and outgoing objects automatically for you.
- Real closures like Ruby; closures are like anonymous methods that you can pass around as first-class objects:
list a = ["Tom", "Ter", "Tim"];
a:{string s | println(s);}; a:println;
- Streams of objects the map operator, ':', applies a closure or method to a stream of objects. Many objects know how to turn themselves into a stream such as files and strings. For example the following iterates over the characters in a string:
"abc":{char c | print(c);};
- Subsets of data structures or streams:
list goodones = [1,99,20];
subset = names[goodones];
- List comprehensions are easy using map:
list a = ["Tom", "Ter", "Tim"];
list b = a:{string n where n!="Ter" | return n;};
- Mixins; like Ruby mixins. Mixins behave like abstract superclasses except that a class may include more than one mixin
- Dynamic mixins and delegation; you can add methods to classes at runtime.
int.mixin("toHex",
{ int self |
return java {new mstring(Integer.toHexString(((mint)self).v))};
}
);
println(32.toHex());
- Type annotations; you specify the type of variables without forcing static typing. The type information is used to generate faster code and make Mantra code more readable.
- Pipes; hook the output of one actor to the input of another. The first and last element in the pipeline are sources and sinks of data. The following example computes the word frequency count from a file:
dict wfreq = dict();
File f = File(filename);
f => Words() => { string w |
mutint c=wfreq[w];
if ( c is null ) wfreq[w]=mutint(1);
else c++;
};
The pipe operator also launches a thread for each operand of a pipe and calls the predefined main() on each object. The significance of POP lies in that you can create new functionality by piping actors together rather than adding methods to classes. Because the pipeline automatically opens and closes streams, you get a really terse statement sometimes. Here is a file copy:
- trees are native data structure like:
1 at root with two children: 2 and 3.
- StringTemplate integration
ST s = `public <name> = <init>;`;
s["name"] = "Ter";
s["init"] = "34";
println(s.toString());
"Fixes" to Java
There are a number of things about Java that have come to annoy me, which I tried to fix in mantra:
- no "public static void main(String[] args)" required to execute some code. Here is a simple program:
println("Hello, world!");
- Java has no decent support for pointing at functions. I hate anonymous inner classes. Mantra has proper closures.
- Mantra does not enforce throws requirement. A code block throws an exception or not; no compile error for not catching or saying method throws.
- Mantra is dynamically typed and consequently has no typecast!
- The number of Java libraries has exploded but the quality, convenience, and speed have not. For example, it is still many lines of code to get the contents of file. That's unbelievable after 12 years. In Mantra, do this: File("foo.txt").contents().