Thursday 12 June 2008

The Fantom language - Is it JavaNG?

For many moons there have been discussions about what we need to do to 'fix' Java. But what might the language look like if we applied all the possible fixes?

The Fantom programming language

Many (all?) of the ideas for creating a 'better Java' are not new, and have been talked about many times over the years. Ola Bini produced one list, which included no primitives, enhanced generics (no angle brackets), closures, method references, implementation in interfaces, some type inference, no checked exceptions and non-null variables. This is a good list and mirrors many of the discussions that have been held on this blog.

Of course, this JavaNG/Java3/BetterJava language doesn't exist does it?

Well, perhaps it does.

The Fan Fantom programming language is one I've been watching for a couple of months since it first launched its website. Its statically typed like Java and Scala, although there are some dynamic features.

I've been meaning to blog about it for some time, but Cedric beat me to it (and its a good writeup too).

Basically, Fantom fixes 95% of the pain points in Java in a manner and style that is close to that which you'd naturally pick if you were creating JavaNG/Java3/BetterJava. Here is a quick rundown of the key features and changes:

Pods, Types and Slots

The basic unit of grouping code is the Pod. This is like a combination of a Java package and module. It forms a key part of the name of any type in the same way as the package name does. But because it is also the module, it is easy to find missing classes - its like every class file knowing what jar file it is stored in. There is also a module level access specifier ('internal'), which is a cross between package scope and proposed 'module' scope in Java.

The two main types of type are classes and mixins. Classes are as expected, with single inheritance. Mixins are like interfaces, but can have implementation too, providing convenient and safe multiple inheritance if required.

Slots are the contents of classes and mixins. There are only two types of slot - fields and methods. All fields behave as properties, and are accessed via methods. You don't need to manually write the getter/setter, but you can if you need to, such as with calculated fields. Methods are pretty much as expected, except that you have to declare them as virtual in order to allow them to be overridden (and you have to declare override if you are overriding).

One key point is that all slots have unique names. This means that a field cannot have the same name as a method. This is a Good Thing, and allows method/field references to just reference the slot name (contrast this with the FCM method reference spec).

It also means that methods cannot be overloaded! But this is also a Good Thing, when combined with default values for method parameters, for example either of the last two arguments may be omitted and will default to zero:

Date withDate(Int hour, Int minute, Int second := 0, Int milli := 0) {
  ....
}

The totality is a very simple, but consistent, basic structure.

Constructors

Constructors are defined like static methods, but use the method name 'make' by convention. This method name and design is designed to allow easy switching from creation of objects to returning cached objects (or specialised subclasses) without affecting the caller:

class Date {
  new make(Int hour, Int minute, Int second := 0, Int milli := 0) {
    ....
  }
}

The 'new' keyword indicates that the method is a constructor.

Assignments and operators

All assignments use the := symbol. This clearly separates them from any other usages of =. It also enables a basic form of type inference when declaring local variables:

  value := 6
  str := "Hello world"

Note that there was no need to declare the variable type (Str/Int). Declarations of fields still require the type to be specified though.

Fantom also supports operator overloading by delegation to methods. Thus, writing a method with a special name will cause the matching operator to be allowed.

As a result, there is no need to call a.equals(b). The == operator is mapped to the equals method - this one change makes code a lot neater.

Immutability and Threading

Immutability (using the 'const' keyword, although a different meaning to C) is built into the language. Fields and classes can be immutable, as these gain extra power in the language.

The main benefit is with multi-threading. The language prevents there being any shared mutable state between two threads. Only immmutable objects may be freely passed between threads. If you need to pass a mutable object to another thread, then it must be serializable (using the built in serialization) and it will be sent to the new thread as a serialized message There is thus no synchronized keyword.

Collections and Closures

Collections and Closures are the only places where anything like generics appears. The collection classes have dedicated type signatures, which look like Java array signatures:

 String[] listOfStrings := ["one", "two", "three"]
 String:Int[] mapOfStringsToInts := ["one": 1, "two": 2, "three" : 3]

Note that both the list and map also have a literal syntax to define initial contents.

Closures are built in, and the API is written to work with them. The syntax is modelled after Ruby, but is pretty readable to a Java developer, using pipes to define the closure arguments:

 list := ["one", "two", "three"]
 list.each |Str val, Int index| {
  echo("$index = $val")
 }

Note how local variables can be accessed and output from within a string (like Groovy amongst others).

Dynamic coding

Fantom encourages more dynamic coding styles. If you use the dot to call a method (as per normal Java) then the method is compile time checked. If you use the arrow operator, then the method is called by reflection. Further, if the method called by reflection doesn't exist, then this error can be caught (the 'trap' method) which is like the 'method missing' concepts in other languages and enables powerful DSLs to be written.

  obj.doStuff()   // compile-time checked call, as per Java
  obj->doStuff()   // runtime reflection call

This can also be thought of as enabling duck typing.

API

Fantom has its own API. It does not directly expose the Java API, although you can access it via the 'native' keyword. The benefit if this is the removal of all the bad and broken parts of the Java API.

The Fantom authors philosophy is to provide all the useful features of the Java API, but in less classes. Thus, the IO API consists of just 4 key classes, with byte/char operations on the same class, and buffering assumed.

Odds and ends

Lines do not need a semicolon at the end. A newline will suffice.

There is built in serialization to a JSON like syntax. This is actually a subset of the language, and can be used to initialise objects at creation time in normal code.

Methods may have convariant returns like Java. But they can also have Self type returns.

Methods can be declared to run 'once', and then cache their value.

If, while, and basic for loops are the same as in Java. Switch statements are better, as they can't fall-through.

Exception handling is as per Java, with try, catch and finally. All exceptions are unchecked (yay!)

The default type for numbers with a decimal point is a BigDecimal equivalent, not a double equivalent. This will mean a lot fewer numerical errors.

Fantom runs on the JVM and the .NET runtime. It manages this by writing to a temporary intermediate format, which then gets further compiled to the right bytecode.

The website is really good and detailed.

Comparing to Java

Fantom is not Java, it is its own language. Yet, it is many, many ways the natural result of applying all the changes that the blogosphere asks for in Java. Just for that reason it pays to evaluate it closely.

Is Fantom a good language? The answer is a qualified yes. At the moment, it looks like a very well designed language I have very few points of contention with it - the main one being there is no non-null support. Minor points include some choices for coding standards (open curly brace positioning, and when to use method parameters).

Is Fantom a new language? Not really. It is a consolodation language, which is what James Gosling claims Java was (ie. a language that doesn't invent much that is radically new. Most of the ideas have been seen elsewhere, but Fantom has a particular JavaNG feel about it.

What are my key features? Built in modules, immutability and safe threading, closures, and a really great solution to generics (ie. only support them on Lists, Maps and Closures). The local type inference, no semicolons, == for equals and interpolated string are four minor features that make a big difference too.

Summary

Fantom is worth checking out. Whether it suceeds as a language is up to many factors - it needs an IDE for example, and a lot of luck.

The key point for me is that Fantom represents much of what JavaNG/Java3/BetterJava would look like if all our ideas were adopted. And while it has many similarities to Java, there is also quite a sense of difference. Perhaps, the biggest aspect of this is that the APIs are different. But that is perhaps inevitable if you want to get any real benefit from closures and fixing generics (by simplifying them).

And that perhaps gives us the definition of where JavaNG/Java3/BetterJava ends and BeyondJava starts. If the language is based around the Java APIs, its a JavaNG/Java3/BetterJava language (eg. Groovy). If the language has its own APIs, its a BeyondJava language (eg Fan, Scala).

All opinions welcome on Fantom and the BetterJava to BeyondJava boundary!

15 comments:

  1. If you dislike Java so much and your whole blog is dedicated to changing things or replacing java, why did you add your blog to javablogs?

    ReplyDelete
  2. Hi Stephen
    James here from JavaLobby. This is a great article - would you mind reposting this on JavaLobby? Let me know.

    James

    ReplyDelete
  3. hehe, its called 'fan'

    ReplyDelete
  4. Darn, those are some really good language choices. If it were supported in IDEA and compiled to fast byte code I would start using it right away..

    ReplyDelete
  5. It reminds me of Eiffel (http://en.wikipedia.org/wiki/Eiffel_(programming_language)) in the way it handles properties and uses the := operator. I like the : for generics and the [] syntax for lists and maps, the "$varname" in strings is nice too.

    So you can't overload a method. But you can overload an operator. Thats a weird combination. You can still override a method in a sub class right? Also unchecked exceptions only? I've lived enough nightmares with unchecked exceptions that I'm wary of a language that doesn't give me full control over the execution path.

    All in all it does look interesting.

    ReplyDelete
  6. Fan is an interesting language that is obviously heavily influenced by C#. The one compelling feature of C# 3.0 that is missing, however, is LINQ expressions.

    ReplyDelete
  7. So long as you can look up compiled bytecode (or source code) for a closure, you can implement LINQ features. I'm not sure if Fan can do this, but it wouldn't be surprising.

    ReplyDelete
  8. Stephen Colebourne12 June 2008 at 22:21

    Mikael, Fan does compile to bytecode, just indirectly. It compiles initially to an intermediate code, which is then compiled a second time to bytecode. This means that it has the full performance of a statically typed Java/Scala (subject to the all-Object restriction).

    Collin, You can't overload a method except by using default parameter values. You can override methods in a subclass so long as you declare the method with the 'virtual' keyword. You don't overload an operator exactly - you just write a method with a pre-defined name, and the method will be used to as the operator. And I know that the checked exception debate is a touchy one, but there is a reason why no other language has adopted them - they really don't work well.

    ReplyDelete
  9. "Ideas and musings from a Java developer" Huh? You're not a Java developer. You're a traitor who wants to subvert Java with other languages. You academics think you're great but us hard-working Java developers in the trenches don't want operator overloading, closure, or Groovy! Sun needs to GET OFF THEIR ASS and fix JEE, not mess around with closures and LISP! .NET is looking better and better every day...

    ReplyDelete
  10. General comment, I skimmed some JVM bytecode output from Fan. Looks like the pure OO will impact heavy math, but most of the statically-typed code creates what you'd expect. So my guess is that it outperforms current dynamic languages for the common case. Probably nears Java performance. No benchmarks, though.

    ReplyDelete
  11. No sir, I don't like it ;)

    You identified some major pain points but I disagree with the specific solutions you implemented.

    I don't think Java needs any sort of major rocket science. We simply need a more cohesive, minimalistic core to build on top of. Fixing Generics (dropping raw types and backwards compatibility) would be a huge step in the right direction.

    ReplyDelete
  12. That sounds quite nice, but I can't see a single point that you can't have in Scala. On the other hand Scala gives you much, much more, and one thing I really miss in Java is pattern matching. Of course, Scala IS more complicated, but it gives you so much more power without breaking down all bridges to Java...

    ReplyDelete
  13. Stephen Colebourne13 June 2008 at 10:55

    John/Ahkmed, I'm certainly no academic, and Java is my principal programming language (full time day job) - it allows us to work and earn a living day in day out. But that should never preclude us from looking beyond our day to day lives.

    Tom, thanks for looking at the bytecode - that sounds like what I'd expect.

    Gili, I'd like to see a proposal/language that does just 'fix generics'. I haven't seen one I like yet.

    Daniel, yes Scala can do many, many things and provides much power - but that may well be its downfall. At the moment, it is the 'cool' language, which I think is unfortunate, as I don't believe its the next mainstream choice.

    ReplyDelete
  14. Scala is too complicated i agree but it has many good ideas, structural types is one (typesafe almost duck typing), possibly traits.

    I wont ever consider a language that removes checked exceptions.

    ReplyDelete
  15. 194.117.22.135, sounds like you are stuck with Java, then, because no one else wants checked exceptions. I don't think I've ever seen them anywhere else. And it's not for lack of exposure. (There must be some obscure cases with checked exceptions, I suppose, but I've really never seen them that I can remember.)

    ReplyDelete

Please be aware that by commenting you provide consent to associate your selected profile with your comment. Long comments or those with excessive links may be deleted by Blogger (not me!). All spam will be deleted.