It is always good to have a point of reference. Google recently published their own language for the Java Virtual Machine, Noop <http://code.google.com/p/noop/>. How do dodo <http://dodo.sourceforge.net> features compare to the proposed Noop features? Read on.
Fundamentals
------------
* No primitive types, everything is an Object.
-- The dodo type system can manage both, as an Object is simply a structure which is Polymorphic
* Strong typing
-- Dodo has strong typing.
* No optional syntax: semicolons and parenthesis required
-- Dodo does not require semicolons to write code.
* Has properties
-- All instance variables are really getters in dodo. There is no concept of setter. On the other hand, property values can be set when deriving a prototype or by declaring the type is Mutable.
* No facility for statics
-- Dodo has class variables. Static variables can be simulated by private globals which are allowed.
* Executable/compiled documentation as much as possible
-- This is not part of the dodo specification but an interesting idea.
* Classes have retained metadata
-- This is not part of the dodo specification at the moment.
* There is always a seam between any pair of classes, for testing
Readability
-----------
* Always be consistent
-- Dodo is a multi-paradigm language and as such the syntax may confuse some.
* Don't use shortcuts or syntax sugar like underscores-for-variables or leaving off parentheses on method calls. These make it quicker to write, but harder to read, especially for a code reviewer who is not expert in the language but knows C++ or Java well.
-- Depending on the coding style, a dodo program will be more readable for different groups of programmers.
Good stdlib
-----------
* Pick best implementations from other languages
-- We are not there yet... Will see later. One major difference with existing libraries is that access to the system is done through services, which can be implemented in a different language.
* Use JodaTime for Date/Time apis
* Use util.concurrent for concurrency
* Expose Google collections
* allow concurrent collection iteration when given a ConcurrentIterator?
* Introspection done objective-c style (easy, few lines of boilerplate)
-- Definitely want easy introspection if it is offered.
Injection
---------
-- This is something that is not much a concern in dodo. It is easy to define a type with the desired properties.
Considering the example on the Guice slides, you could do:
def Tweeter = new SmsTweeter()
def Shortener = new TinyUrlShortener()
...
def TwitterClient = new TwitterClientTemplate(tweet: Tweeter, shorten: Shortener)
-- As for default variable values, it is even simpler as dodo is prototype-based. To take the example from ProposalForKeyTypes, you could do:
def port = 9876
...
class Server {
port portNumber #defaults to 9876
}
-- Then we could imagine overriding the 9876 constant with a different value at runtime, similar to the -D option in Java.
* Use Guice or PicoContainer under the covers
- Need to think hard about construction - can we use PicoContainer's greediest constructor? Is there always a default constructor?
-- Dodo types always have a default constructor.
- Instead of greediest constructor approach, use optional injectables?
- Need to examine scopes carefully. Guice scopes can't be concentric without a lot of work. Pico just uses parent containers/injectors for scope. Guice can work that way... how do we want to do this?
* A type is either newable or injectable, newable types cannot have injectable deps? See ProposalForNewableVsInjectable.
* A first-class binding operator: Service -> ServiceImpl
* Injectors/scopes as first-class language constructs
Immutability
------------
* final is the default, use the mutable keyword to make a variable which may change its reference.
-- The only dodo type which is mutable by default is the array or map.
* implement const?
-- Function arguments are always constant in dodo, unless they use the "." reference operator. There is a distinction between a function -operates on constants, no side-effects- and a method/constructor -can take references and have side-effects.
-- The const transition function puts a mutable variable in readOnly state.
* encourage functional style by having built-in predicate, filter, etc on collections, as well as convenient functions/blocks/closures/lambdas
-- Dodo offers map, filter, fold/seed, unfold and match as built-in constructs operating on arrays. Dodo has a (CPS) notation for lambdas. Functions can be written concisely in functional style.
Strong inferred typing
----------------------
* Avoid null, Types shouldn't allow null value by default
-- Null is not part of the dodo specifications. Variables always have a default value.
* Maybe Option<T> to create a nullable T, like Scala. See NullObjectPatternProposal.
-- In Scala that is mainly used to select alternative instructions AFAICT. In dodo you would use continuations/throw for that.
* Inferred types? foo = "bar" means foo is a String
-- The dodo syntax for inferring the type in a declaration is:
def foo = "bar"
Exceptions
----------
* Only unchecked exceptions
- Maybe a way to indicate errors to the caller aside from unchecked exception, see ProposalForErrors.
-- Dodo is implemented in continuation-passing style (CPS), so there should be no overhead for "exceptional" code paths. On the other hand a stack trace could be a little difficult to follow as it would be based on possible continuations rather than frame pointers.
Class parameters and properties
-------------------------------
* class Foo(Bar bar) {} defines the default constructor, has a read-only property bar, like Scala. See ProposalForFirstClassProperties.
-- There are two possible approaches to class parameters in dodo. One is to declare a class property with the syntax:
class Foo {
Bar Foo.bar
}
-- The other is to create a class template, which require a parameter to make it concrete. The syntax is:
class template Foo(bar: $someBar) {
# Use someBar...
}
-- In both cases, you can create a specialised version of the class with:
def MyFoo = new Foo(bar: myBar)
Foo((bar: myBar)) # Shorthand notation equivalent to MyFoo
Testing
-------
* test keyword allows compact syntax for declaring nested test suites with tests as the leaves. No need to use classes and methods to declare test suites and tests. See ProposalForTestingApi.
-- There is no keyword for tests in the dodo specification. However it would be beneficial to include testing facilities in the standard library. The test in ProposalForTestingApi could then be written:
testsuite Test # declare somewhere visible
class template MyClass(con: $c)
{
def printThing() {
console!c.Puts("thing")
}
}
# use FakeConsole as parameter of MyClass for this unit test
def Test.MyClassTest = new UnitTest(underTest: new MyClass(con: FakeConsole()), message: "I'm testing MyClass")
{
MyClassTest()
{
unit.printThing
test {unit.c.printedText = "thing" {Log("printed text = \"thing\"")}}
}
}
# main program
def Main {
Main() {
Test() # Test constructor, runs the unit tests
}
}
* Allow test blocks within production code, so the tests are right next to the code-under-test. Probably want to strip that from non-debug/non-test compiles.
-- Non-reachable private code can always be stripped away by the compiler
* tests have some "friend" relationship with the class under test, to allow whitebox test of private methods
-- In dodo, that means the test has to be in the same file as the class under test
* Every instantiation goes through the injector, so there's always a seam between every pair of classes
-- Not sure I understand that part. Classes should be coded for testing, like MyClass in the above example which is parameterised. It would also work with a class property in this case. An alternative is to replace the services used (here "console") with testing versions, but that approach is quite limited as it applies only to services.
Public API
----------
* need some way to enforce public API separate from visibility of types and methods. Maybe don't need "private" at all
-- Dodo does not offer "private", everything declared in the header is public and everything declared in the source is private.
* Android has an interesting approach: create interface jars which have the implementations stripped from public classes and don't contain private stuff at all.
-- In the header part, only the initial value of a variable and function code are private. All declarations are public.
Literals
--------
Lots of useful literals!
* String is a type literal
* "Hello" is a string literal
-- No mention of a character literal, use Char.for("a") or Char("a") in dodo (no character literals). String literals are the same as Noop.
* """line1\nline2""" is a multi-line string literal
-- I understand that having a special syntax for multi-line strings prevents annoying syntax errors, but I am not sure it is that important when we have syntax highlighting in editors.
* /.*/ is a pattern literal
-- Dodo has more types of pattern literals.
* { "a": "b", "c": "d" } is a hash literal (should evaluate to most specific type possible)
-- This is ["a": "b", "c": "d"] in dodo.
* [1,2] is an array literal
-- Same syntax in dodo.
* `http://google.com` is a URI literal (like Fan, is it useful?)
-- Looks nice, but URI.withPath("http://google.com") or URI("http://google.com") would work too. Not sure.
Optional to separate bindings from code
---------------------------------------
Can we provide bindings to the injection framework inline, using scopes and @ImplementedBy and such? See PropositionForScopes.
Enforce naming conventions
--------------------------
This can help to improve readability, although we should take care to only enforce widely-agreed-upon conventions.
* No reason the parser should allow types to start with lower case or variables to start with upper case
-- In dodo, identifiers starting lower case are variables (prototypes) and identifiers starting upper case are types.
ProposalForComposition
----------------------
-- In dodo inheritance means both interface and implementation inheritance, which I would usually disapprove. That is in the hope of making the object model less alien for Java or C# programmers. There is nothing stopping you from keeping the inheritance private, though. Also dodo has mixins which it calls qualifiers.
-- I find the proposal confusing. I do not see why a Dog would *not* be an Animal. Since all (concrete) classes have a default constructor it should be painless to construct an instance of the subclass. Supposing Animal is Polymorphic my hierarchy using a qualifier would be:
qualifier Canine {
int numberOfLegs() {return 4} # overrides numberOfLegs in Animal
}
def Dog = new Animal() is Canine
-- Dodo also offers conversion functions, which are not related to polymorphism but can be useful at times. For example:
def Dog = new Animal() is Canine
{
->(Friend) asPetFriend
}
Friend f = Dog() # use conversion function asPetFriend
2009-09-25 13:30:43 UTC by jido
Dodo has a special syntax for intervals. Intervals are used mainly to specify an index range in a list, but they can have more uses. At the moment I envision their use only for enumerable types like integers.
The current documentation contains an example of interval, which is the form:
n+
That notation means "n or more". Used as a list index, it allows to select all elements of the list from index n onwards.
An equivalent is:
n...
You can use the ellipsis with an upper bound too. Consider the notations:
...n
m...n
They represent respectively "up to n" and "from m to n". Used as a list index, the first allows to select all elements up to n. The second selects all elements which index is part of the range.
As lists are usually indexed starting from 1, the size of the list is also the index of the last element. This notation can be used in a list index expression to give the list size:
$#
For list 'a', that is the equivalent of 'a.count'.
Finally, an interval can be expressed as a list of intervals using the standard list notation. For example:
[1, 2, 3, 9...15, 99+]
To conclude, here are some examples of use for intervals.
houses[2+] # all houses in the list except for the first
# Categorize the number as low, medium or high
test match(number)
{
1...4: return low.
5: return medium.
6...9: return high.
}
choices[[1, $#]] # get first and last elements in list
2009-07-30 15:37:05 UTC by jido
The syntax of the dodo language is still a work in flux, but the documentation shows the main orientations.
My main inspirations are C, Python, D and various other languages.
From C I borrowed the variable declaration style, the bracket block syntax, the dot as field accessor and more. The colon-dot block syntax is inpired from Python and the exception system is inspired from D.
I would like to discuss some decisions that were made that depart from these languages.
While many languages are case-sensitive, they rarely attach a semantic meaning to the case of identifier names. Where there are conventions they are not enforced by the language. For example you can find the following advice on a page addressed to Java programmers:
Package names should be pure lower case. Class names should begin with an upper case letter. Violating this convention will confuse the heck out of anyone trying to decipher your code.
I believe that the conventions are a good thing, and I went one step further by enforcing them in the language. A dodo name that starts with a capital letter is always a class name. That decision helps the syntax parser with some constructs, such as class declaration.
Like Python, the semicolon separator at the end of a line is optional. Because of this all dodo blocks are attached to the previous instruction or keyword, a block by itself is not valid dodo syntax.
Instead of multiplying the keywords that introduce a loop or a test instruction, like C does, I restricted them to the two keywords 'loop' and 'test'. Variations depend on what follows the keyword. The table below shows the correspondence between Java and dodo constructs:
Simple test
Java: if (...) {...}
dodo: test (...) {...}
Test with alternative
Java: if (...) {...} else {...}
dodo: test {... {...} default {...}}
Test expression with matches
Java: switch (...) {case ...: ...; break; default: ...}
dodo: test match(...) {... {...} default {...}}
Infinite loop
Java: while (true) {...}
dodo: loop {...}
Loop while condition is true
Java: while (...) {...}
dodo: loop while (...) {...}
Do instructions and repeat while/unless condition is true
Java: do {...} while (...)
dodo: loop until (...) {...} (the condition is inverse of Java condition)
Loop with initialiser, condition and step
Java: for (...; ...; ...) {...}
dodo: loop for (...; ...; ...) {...}
Loop for each element in a list
Java: for (...: ...) {...}
dodo: loop foreach (... in ...) {...}
I know that this choice will displease some, however I believe that it keeps the program readable without cluttering the namespace.
In contrast I reserved a relatively large number of keywords for functional constructs: if, else, match, fun, map, fold, seed, unfold... This aims at making dodo a convenient language for functional programming. While dodo is targetted at developers with an imperative programming baggage, I would like very much to ease the transition to functional programming for those that wish.
In line with this there are two syntaxes for function bodies in dodo. One is similar to C functions, the other is similar to C variables. Variables are defined by their type, their name and an initial value which is an expression. Using an expression as function body is akin to doing functional programming. An example:
int square(int x) = x * x
Finally, while I am not decided to include the full type inference which is widespread in functional languages, a limited form of type inference is available for variables with an initial value.
2008-08-16 20:01:03 UTC by jido
The dodo project just quick-started. There is now a draft documentation of the language available at the project home page and in the Documentation area.
2006-11-10 12:16:14 UTC by jido