Friday 18 November 2011

Guide to evaluating Fantom

Yesterday at Devoxx I spoke about the Fantom programming language.

Evaluating Fantom

This blog post provides some links to help you evaluate Fantom, and could be useful if you attended the session yesterday, or if you're generally looking at languages.

While the "hello world" example is useful when evaluating a language, its important to look deeper. Key areas to look at include the type system, paradigm (OO vs functional), immutability, concurrency, standard library, modularity and composability. Instead of hello world, here is a word counting program:

class WordCount {
  Void main(Str[] args) {
    if (args.size != 1) {
      echo("usage: Wordcount <file>")
      Env.cur.exit(-1)
    }

    // Set up an empty map to count each word, setting default for each value to zero
    wordCounts := Str:Int[:] { def = 0 }

    // Open the file, read each line in order
    file := Uri(args[0]).toFile
    file.eachLine |line| {
      // skip empty lines
      if (line.trim.isEmpty) return

      // split and trim on whitespace into words
      words := line.split

      // count each one
      words.each |word| { wordCounts[word] += 1 }
    }

    // Show each word found, with its count, in alphabetical order
    wordCounts.keys.sort.each |key| {
      echo("$key ${wordCounts[key]}")
    }
  }
}

Even without the comments, a key Fantom feature is that the code is instantly readable. It is very much in the mainstream of syntax and there is a high chance that developers can read the code without learning the language.

Underneath, I think Fantom is more radical in some ways than many other new JVM languages. Fantom is effectively its own platform on top of the JVM. It compiles to its own bytecode format, fcode, with JVM bytecode emitted on demand. It can also compile to JavaScript, and people have investigated .NET and LLVM compilation as well. And, although you can call Java classes, most of the time you work with Fantom's API, not Java's API.

The biggest difference to other languages is the type system. Static typing is valuable for communication, documentation and basic error checking. But most static type systems are a straight-jacket of rules that effectively require you the developer to prove your application to the compiler, something which Java generics has shown can be very painful. Fantom's approach is radically different - it uses the type system to support you, rather than restrict you. This is what Brian Frank, the language author had to say:

Kotlin seems to be following in the same basic philosophical path of Java, Scala, and most all other statically typed languages: the job of a static type system is to always prove that something is type safe at compile time. I believe Fantom is unique in our approach of type systems: we allow things which might work to be caught at runtime versus compile time. This is a pragmatic trade-off which years of use seems to validate: you can use a weaker but much simpler type system and still catch most errors at compile time. Fantom proves that there can be a sweet spot between static and dynamic type systems.

Whether you can accept this approach to the type system will define your evaluation of Fantom. If you can appreciate the idea that you don't need to add more generics to fix the problems in Java, then you will appreciate Fantom. If you've used and liked the freedom of dynamic langauges, like Ruby, but understand that a little static typing could be useful, then you will appreciate Fantom.

Fantom also does something which every other new language should do, but doesn't. Everything is a module, which provides big knock on benefits to reflection and dynamic behaviour, plus a good way to assemble code.

Finally, Fantom tackles concurrency properly by actively preventing shared mutable state (something that Scala fails to do). Immutability is deeply built in. Every object, list, map and function knows if it is immutable, and lists/maps can be converted to be immutable. At the static level, there are no static variables, and constants must be immutable. The only way to access another thread is via the actor framework, and that only allows you to send an immutable object or a copy of a mutable one (via Fantom's built in serialization).

Summary

When Java was created it took C and C++ and threw lots of cruft away to create something radically simpler, yet more powerful overall. Fantom has done the same to Java, throwing lots of cruft away to create something radically simpler, yet more powerful overall.

Hopefully the links and this quick feature guide will help those looking at the language.

10 comments:

  1. really digg the closure syntax. I wonder why this wasn't a front runner for Java 8's closure syntax .. now pretty much a copy of what c# did.

    ReplyDelete
  2. // skip empty lines
    if (line.trim.isEmpty) return

    Any language that uses "return" to mean "break from this iteration of the loop" isn't very intuitive to me.

    ReplyDelete
  3. "wordCounts := Str:Int[:] { def = 0 }"

    You find that readable???

    ReplyDelete
  4. @Anonymous1, once I thought about it, I came to the same conclusion. With the FCM # syntax rejected, I think this would have been a good alternative.

    @Neal, so C# isn't intuitive? AFAICT it uses return that way, ie. return from a closure.

    @Anonymous2, the challenge is whether you can roughly work out what the whole code does - a hello world is never challenging enough, and extracting a single line misses the point. The := declares a variable. The [:] is an empty map. The Str:Int declares the type, like Map. The curly braces initialise the object, in this case the def property.

    BTW, if it were my choice, I'd use val/var rather than := to declare a variable.

    ReplyDelete
  5. return is indeed a bit surprising but it makes sense if you think about it (Neal, I'm sure I don't need to tell you that). The harder part in the example is indeed the literal initializer for collections, i.e. [:] for an empty map, [,] for an empty list.

    Overall, I still think these are very minor readability bumps that one can get used to in a few hours to the point of not even thinking about them ever again.

    I still find Fantom extremely readable compared to the competition but looking forward to seeing Kotlin enter the fray.

    ReplyDelete
  6. Fantom's Brian Frank wrote up a good, short overview of the language here: http://drdobbs.com/229218754

    ReplyDelete
  7. or you could go c#

    private static void Main(String[] arguments)
    {
    File.ReadLines(arguments[0]).SelectMany(x => x.Split(' ')).GroupBy(x => x).ToList()
    .ForEach(group => Console.WriteLine(@"{0} {1}", group.Key, group.Count()));
    }

    ReplyDelete
  8. > wordCounts := Str:Int[:] { def = 0 }

    > "a key Fantom feature is that the code is instantly readable"

    Er, that's maybe because you commented the line. Otherwise I would not have had a clue what Str:Int[:] meant

    ReplyDelete
  9. Darren, you win!
    I might be a bit bias being a C# developer, but almost always when Scala or any of the other curly brackets based new JVM languages comes up, I feel that C# does it better. Sure, there is some just-for-the-compiler code in it that you could throw away, but in the end I hold it as a winner.
    That said, I do like alternatives, but more like Erlang and Ruby.

    ReplyDelete
  10. Anonymous, that C# is very similar to the equivalent Scala:

    object WordCount extends App {
      io.Source.fromFile(args(0)).getLines.flatMap(_.split(' ')).toList.groupBy(a => a).foreach(group =>
        printf("%s %d\n", group._1, group._2.length))
    }

    ReplyDelete