📜 ⬆️ ⬇️

Why fantom?

Fantom is an object-oriented, statically-typed general purpose language developed by the Frankie brothers ( Brian Frank, Andy Frank ). One of the key features of the Fantom is a powerful standard library, abstracted from the specific environment in which it will be executed. At the moment, code written in Fantom can be run in the Java Runtime Environment (JRE), the .NET Common Language Runtime (CLR), or compiled into JavaScript code.

class HelloWorld { static Void main() { echo("Hello, World!") } } 

Portability


The main reason for creating the Fantom was writing software that can run on two Java VM and .NET CLR platforms. The reality is that most companies develop their software for one of these platforms. Even dynamic languages ​​like Python and Ruby run on one of these virtual machines. Fantom was created to solve the problem of portability from one virtual machine to another. Fantom source code is compiled into fcode - bytecode, which can easily be translated into Java bytecode or IL. Transmission occurs at run time, which allows you to deploy the Fantom module as a separate file and run on any VM.

Portability means much more than just Java or .NET. As mentioned above, Fantom can be compiled into JavaScript to work in browsers. At the same time, the Fantom is not going to stop there; the following goals are Objective-C for iPhone, LLVM , Parrot .
')

Elegant API


Although the tastes and do not argue ("Beauty is in the eye of the beholder"), the creators of Fantom truly obsessed with beautiful and convenient API. One of the basic principles of the Fantom. Java and .NET have one common tendency of maximally dividing the functional into small independent and abstracted units (classes). Fantom has the opposite philosophy - they believe that you can do with a small but powerful number of units.



A good example is the java.io package, which contains more than 60 classes and interfaces, in the Fantom everything you need is in four classes: File , Buf , InStream and OutStream . And this is how its use looks like:

 Void textIO() { f := File(`text-io.txt`) // write text file (overwrites existing) f.out.printLine("hello").close // append to existing text file f.out(true).printLine("world").close // read text file as big string echo(f.readAllStr) // read text file into list of lines echo(f.readAllLines) // read text file, line by line f.eachLine |line| { echo(line) } } 

Typing


The whole world has split into supporters of static and dynamic typing. The creators of Fantom believe that both sides are extremely critical of each other and choose the middle ground between them - a moderate approach to the typing system.

On the static typing side, Fantom requires a description of the fields and method signatures with types. It is a good practice to record the format of communication between components. For example, if I want to write a method that works with a string and a number, then this should be fixed directly in the code. Unlike static typing of method and field signatures, in the code it often only interferes, forcing you to write unnecessary code. Type inference in the Fantom avoids this problem. For example, bulky dictionary creation in java:

 Map<Integer, String> map = new HashMap<Integer, String>(); map.put(1, "one"); map.put(2, "two"); 

Equivalent to the Fantom single line:
 map := [1: "one", 2: "two"] 

But sometimes you really need dynamic typing, so one of the key features of the Fantom is the ability to invoke a method using static or dynamic typing. If you call a method with the "." Operator, the call will be checked by the compiler and compiled into effective machine code.

On the other hand, you can use the "->" operator to specify a dynamic call. In fact, "->" will be redirected to the Obj.trap call. By default, trap works like a ".", But only at run time. You can change this behavior by defining your dynamic design.

 if (a is Str) { return a->toInt } obj->foo // obj.trap("foo", [,]) obj->foo(2, 3) // obj.trap("foo", [2, 3]) obj->foo = 7 // obj.trap("foo", [7]) 

Generics


Interestingly, while Fantom is trying to make the code less typed, Java and C # go in the direction of more strict typing, generics illustrate this trend. A fully parameterized system is directly related to the complexity of the system, so Fantom is now trying to find a balance between benefit and complexity.

Currently, Fantom has limited generic support - the user cannot use his own. However, the three built-in classes can List, Map, and Func. For example, the list of integers in the Fantom is declared as int []. The creators of Fantom believe that they are in the middle: generics are, but without complicating the system.

Impurities


The question of comparing the model from the domain to the code is one of the most frequently solved issues in software development. Usually in object-oriented programming, this phrase means modeling classes and interfaces. Java and C # use the same approach: classes support single inheritance, multiple inheritance interfaces, but do not support implementation inheritance.

Anyone who has worked with Java or C # knows that the choice between creating a class or interface is very important. Because if you choose a class, then you use your only chance of inheriting the implementation. If you have a large and complex model, then the interfaces become an additional burden. For example, if there are two objects with different heirs, and equally implementing the same interface, the functionality will have to be duplicated. In addition to duplication, there is a problem changing the version of the interface, which affects all implementations.

There are many good reasons why Java and C # use the class / interface model. Multiple inheritance opens many doors, but this happens due to the increase in complexity and rather unpleasant nuances. The fantom again occupies the middle, called the mixins. Impurities are interfaces that an implementation can store in itself. To avoid errors of multiple inheritance, the impurity is limited to some functions , such as fields that store the state. Example of impurity:

 mixin Audio { abstract Int volume Void incrementVolume() { volume += 1 } Void decrementVolume() { volume -= 1 } } class Television : Audio { override Int volume := 0 } 

If interested, the java equivalent can be viewed here .

Modularity


Modularity is an important aspect of software that is needed by a modern programming language.
Unfortunately, the last decade in java, we are experiencing hell with the classpath . In addition to problems with the classpath , java made the wrong decision and switched to a J2SE monolith of 44Mb in size, which significantly slowed down our applications.
In .NET, they approached this issue very seriously, therefore, due to the versioning mechanism, the GAC and other means, some of the problems from Java were solved. But they lost the simplicity of the zip module and started packing the modules in a DLL, which contain many more different parts, which makes it difficult to work with the module.

In the Fantom, everything is built on modules called pods. As in java, under - this is just a zip file that can be easily viewed. Meta data is stored in the special file /meta.props , which is a key-value type entry, such as pod.name, pod.version, pod.depends, and so on . Dependencies are stored in its metadata and are described in an explicit and understandable form.

To organize code in the namespace in java, packages are used, and the jar file is treated as a module. The inconsistency between these concepts causes a big problem. You have the name of the java class, but it does not tell you in which jar file this class lives and where to load it from.

The Fantom made a simple decision to manage the names: three levels of hierarchy in the name “pod :: type.slot”, i.e. at the first level, always the name of the pod, then the name of the type (similar to Class in java) and then the name of the slot (method or field). This consistent namespace behavior makes it easy to manage the build process of large systems.

Functional programming


Java and C # are moving towards full support for closures, but behind them they leave a big mark on the story, as the old API. Fantom was created with the support of closures at the initial stage. Closures are a key feature of the language that is used everywhere and always.

 // print a list of strings list := ["red", "yellow", "orange"] list.each |Str color| { echo(color) } // print 0 to 9 10.times |i| { echo(i) } // create a function that adds two integers add := |Int a, Int b->Int| { return a + b } nine := add(4, 5) // map Int to Str map := [0:"zero", 1:"one", 2:"two"] // empty Int:Str map Int:Str[:] // map [1, 2, 3].map { "f" + it * 2 } // ["f2", "f4", "f6"] //reduce ["2":2, "3":3, "4":4].reduce(0) |Int sum, Int v->Int| { sum + v } // 9 

Declarative description


The best description would be a couple of examples:

 Window { title = "Example" size = Size(300, 200) content = EdgePane { center = Label { text = "Hello world"; halign = Halign.center } bottom = Button { text = "Close"; onAction.add { it.window.close } } } }.open homer := Person { name = "Homer Simpson" age = 39 children = [ Person { name = "Bart"; age = 7 }, Person { name = "Lisa"; age = 5 }, Person { name = "Maggie"; age = 1 } ] } 

Parallelism


Most languages ​​currently have one common pattern between threads. This means that the developer himself must take care of locking the memory. Incorrect locking can lead to unpleasant errors, such as deadlocks, race conditions, etc. All this is quite a low level to concurrency management.

Fantom supports concurrency using the following techniques:
  1. Immutable objects (streaming security)

     const class Point { new make(Int x, Int y) { this.x = x; this.y = y } const Int x const Int y } p := Point(0, 0) // ok px = 10 // throws ConstErr vowels := ['a','e','i','o','u'].toImmutable 

  2. Static fields can only store immutable objects, so different threads cannot access shared variable data.
  3. Message model (actors) for communication between threads (Erlang-style)

      echo("\n--- echoActor ---") // this actor just echos messages sent to it a := Actor(ActorPool()) |msg| { echo(msg); return msg } // send some messages and have them printed to console f1 := a.send("message 1") f2 := a.send("message 2") f3 := a.send("message 3") // now block for the result of each message echo("Result 1 = " + f1.get) // message 1 echo("Result 2 = " + f2.get) // message 2 echo("Result 3 = " + f3.get) // message 3 


Syntactic sugar


  1. Default values for parameters

     class Person { Int yearsToRetirement(Int retire := 65) { return retire - age } Int age } 

  2. Type inference - types of local variables can be output
  3. Implicit field access

     class Thing { Int id := 0 { get { echo("get id"); return &id } set { echo("set id"); &id = it } } } 

  4. Zero Types - the division of types into those that cannot accept null, as a value, and which can.

     Str // never stores null Str? // might store null x := str.size => x is typed as Int x := str?.size => x is typed as Int? 

  5. Removed checked exceptions in C # Anders Hejlsberg also did not include them (and did it right)
  6. Numerical accuracy - there is only 64-bit support for Int and Float


Resources


  1. Fantom eclipse-based IDE F4


  2. Web applications framework Tales
     //Hello world written in tales using tales class HelloWorld : Page{ @Route{uri="/hello-world"} Void main(){ response.writeStr("Hello World") } } 

  3. Logic-free template engine Mustache
     using mustache Mustache template := Mustache("Hello, {{ name }}!".in) template.render(["name":"world"]) 

  4. Fantom Pod repository


  5. fantom.org


Conclusion


A common mistake is the opinion that the Fantom is another enhanced version of Java. But one has only to look at it, like a language with its own philosophy and concepts, as you begin to understand all its charm. In addition to stability, a number of features such as a friendly community , a completely open source code , a pleasant IDE and many more pleasant things can be attributed to the Fantom.

Source: https://habr.com/ru/post/175979/


All Articles