In 2009, my brother Andy and I started SkyFoundry, a software company focused on analytics for the Internet of Things. One of the many exciting aspects of bootstrapping a software startup is that you begin with a clean slate. So we began to think about what programming language we might use to construct our product, and we found the options wanting. We spent our nights and weekends creating ourselves a new programming language, and thus Fantom was born.
I had been building software systems with Java since the 1.0 days, so I was familiar with all Java's strengths and weaknesses. Perhaps Java's strongest asset was the JVM as a mature, bullet-proof runtime. So from the get go, the JVM was our primary target. But we had some guiding principles on where we thought Fantom should break new ground:
- Stick to Java's statically typed object-oriented core, but integrate functional programming and dynamic typing
- Ability to target alternate runtimes such as .NET and JavaScript
- Tackle big problems that plagued Java programs: concurrency bugs and null pointer bugs
- Built-in modularity to aid the construction of large software systems and avoid issues like classpath hell
- Fantom should be licensed and developed as an open source project.
What we wanted to keep from Java was the syntax which was approachable and readable to a wide audience of everyday programmers. Fantom is at its heart a statically typed object-oriented language much like Java or C#. In addition, functional programming is blended into Fantom with support for first class functions and closures. The whole library is designed to use closures for easy extensibility. For example,
// find all the employees who have a salary over $100,000 highPaid = employees.findAll |e| { e.salary > 100_000 } // sort a list of files by modified time files.sort |a, b| { a.modified <=> b.modified }
In the first example, we pass a closure function to the findAll method. This closure is called for each item in the list and returns true or false to match the item. All the matched items are returned as a new list. In the second example we see how to use a closure to provide a customized sorting comparator. Judicial use of functional programming is a key ingredient in the design of Fantom's easy to use APIs.
We love static typing and find it a useful tool in our toolbox. But there are definitely times when a static type system hinders an elegant solution. In Fantom, we wanted the best of both worlds, so we designed two different "call" operators. The "." operator calls a method with static typing and the "->" operator calls a method with dynamic typing. Using the "." operator lets you reap all the benefits of static typing: compile-time checks that the method exists and that your parameters are all correct. But if the compiler is getting in your way, just switch to "->". This duality between static and dynamic method dispatch lets us have our cake and eat it, too.
Portable Runtime
Java deserves much credit for making "portable programs" a mainstream concept. However, licensing restrictions and the huge surface area of Java's standard library make porting the Java runtime a sticky situation. Witness the current Oracle lawsuit against Google regarding Android to see how Java isn't necessarily the best vehicle for portability. And despite more than a decade of trying, Java never made any traction as a suitable runtime inside web browsers. In Fantom, we deliberately designed the language and the standard library to be portable to alternate runtimes.
Fantom supports three target runtimes: Java VM, .NET CLR, and JavaScript. The JVM runtime is the most mature; Fantom code on the JVM runs with the same performance as most Java code. The .NET runtime is completely functional, although it tends to lag in maturity. The most interesting alternative is the JavaScript runtime. Fantom code can be compiled directly to JavaScript for execution in web browsers. Most of the core standard library is available, too, as JavaScript including Fantom APIs for working with the DOM.
Consider the emerging model of web applications: increasingly a large percentage of a web application's codebase is dedicated to frontend JavaScript. It seems likely that in this new decade we will revert to a traditional client/server model of development, with a web application's codebase roughly split evenly between server and client. The only difference from the 90's is that the client will be JavaScript code running in a browser. Writing your client and server code in two different languages is bound to lead to duplication and maintenance issues as projects grow in sophisticated. With Fantom you can write your entire codebase in one language. On the server, run your Fantom code using the JVM to maximize performance. On the client, you can leverage Fantom's JavaScript runtime create powerful HTML5 user interfaces. Data structures, validation, and HTTP messaging code can all be written once using a single codebase. We believe this is the future of web applications, and one of the killer features of Fantom.
Concurrency
Perhaps the biggest quality issue that plagues Java programs relates to concurrency. Java's architecture of shared memory with manual locking is prone to race conditions and deadlocks that befuddle even expert programmers. Concurrency bugs are especially insidious because they often slip through testing, only to be discovered in production systems.
Fantom was designed to address concurrency by making it impossible to share mutable state between threads. Fantom achieves this using a variety of features. The most important concurrency feature comes from Fantom's immutable types, which have compile-time guarantees that once an instance is constructed, it will be deeply immutable for its lifetime. Fantom also comes bundled with its own actor framework. All these features are seamlessly integrated to make Fantom ideal for designing highly concurrent systems. At SkyFoundry, we have built databases, web servers, message queues, and protocol stacks — all completely in Fantom. The result has been high performance, high quality software with none of the headaches and hours spend debugging low level race conditions and deadlocks. Let's look a simple example of a const
class:
const class Library { const Str name const Book[] books }
In the class above, the const
keyword informs the compiler that the Library
class should be immutable. Notice that Library
contains a field called books
, which references a list of Book
instances. In Fantom, the compiler and runtime will guarantee that the books
field is deeply immutable — that the list and what the list contains are also immutable.