Почему Fantom?

от автора

Fantom — это объектно-ориентированный, статически-типизированный язык общего назначения, разработанный братьями Фрэнками (Brian Frank, Andy Frank). Одна из ключевых особенностей Fantom — это мощная стандартная библиотека, абстрагированная от конкретной среды, в которой она будет исполняться. В данный момент код, написанный на Fantom, можно запустить в Java Runtime Environment (JRE), .NET Common Language Runtime (CLR) или же скомпилировать в код на JavaScript.

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

Переносимость

Основной причиной создания Fantom было написание программного обеспечения, которое может запускаться на двух платформах Java VM и .NET CLR. Реальность такова, что большинство компаний разрабатывают свое программное обеспечение для одной из этих платформ. Даже такие динамические языки, как Python и Ruby работают на одной из этих виртуальных машин. Fantom был создан решить проблему переносимости с одной виртуальной машины на другую. Исходный код Fantom компилируется в fcode — байткод, который легко может быть транлирован в Java байткод или IL. Транслирование происходит во время выполнения, что позволяет развертывать Fantom модуль, как отдельный файл, и запускать на любой VM.

Портативность означает значительно больше, чем просто Java или .NET. Как было сказано выше, Fantom может компилироваться в JavaScript для работы в браузерах. При этом Fantom не собирается останавливаться на достигнутом, следующие цели — Objective-C для iPhone, LLVM, Parrot.

Элегантное API

Хотя о вкусах и не спорят(«Beauty is in the eye of the beholder»), создатели Fantom по-настоящему одержимы красивым и удобным API. Один из основных принципов Fantom. Java и .NET имеют одну общую тенденцию максимального деления функционала на маленькие независимые и абстрагированные единицы (классы). Fantom имеет противоположную философию — они верят, что можно обойтись малым, но мощным количеством единиц.

Хорошим примером является пакет java.io, который содержит больше 60 классов и интерфейсов, в Fantom все необходимое лежит в четырех классах: File, Buf, InStream и OutStream. И вот так выглядит его использование:

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) }   } 

Типизация

Весь мир раскололся на сторонников статической и динамической типизации. Создатели Fantom считают, что обе стороны крайне критично относятся друг к другу и выбирают середину между ними — умеренный подход к системе типизации.

Со стороны статической типизации, Fantom требует описание полей и сигнатур методов с указанием типов. Это хорошая практика, зафиксировать формат общения между компонентами. К примеру, если я хочу написать метод, который работает со строкой и числом, то это должно быть зафиксировано прямо в коде. В отличие от статической типизации сигнатур методов и полей, в коде она часто только мешает, заставляя писать ненужный код. Вывод типов в Fantom позволяет избежать этой проблемы. Например, громоздкое создание словаря в java:

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

Эквивалентно в Fantom одной строчке:

map := [1: "one", 2: "two"]

Но иногда вам действительно нужна динамическая типизация, поэтому одной из ключевых особенностей Fantom является возможность вызвать метод, используя статическую или динамическую типизацию. Если вы вызываете метод с помощью оператора ".", вызов будет проверен компилятором и скомпилирован в эффективный машинный код.

С другой стороны, вы можете использовать оператор "->", для указания динамического вызова. На самом деле, "->" будет перенаправлен на вызов Obj.trap. По умолчанию trap работает как ".", но только во время выполнения. Вы можете изменить это поведение, определив свой динамический дизайн.

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]) 

Дженерики

Интересно, что пока Fantom пытается сделать код менее типизированным, Java и C# идут в сторону более строгой типизации, дженерики иллюстрируют этот тренд. Полностью параметризированная системы напрямую связана со сложностью системы, поэтому в Fantom сейчас пытаются найти баланс между пользой и сложностью.

В настоящий момент у Fantom ограниченная поддержка дженериков — пользователь не может использовать свои собственные. Однако, три встроенных класса могут List, Map и Func. К примеру, список целых чисел в Fantom объявляется как Int[]. Создатели Fantom считают, что попали в середину: дженерики есть, но без усложнения системы.

Примеси

Вопрос сопоставления модели из предметной области в код — один из самых часто решаемых вопросов в разработке программного обеспечения. Обычно в объектно-ориентированном программирование данная фраза означает моделирование классов и интерфейсов. Java и C# используют одинаковый подход: классы поддерживают одиночное наследование, интерфейсы множественное наследование, но не поддерживают наследование реализаций.

Каждый, кто работал с Java или C#, знает, что выбор между созданием класса или интерфейса очень важен. Потому что, если вы выберете класс, то вы используете свой единственный шанс на наследование реализации. Если у вас большая и сложная модель, то интерфейсы становятся дополнительной нагрузкой. К примеру, если есть два объекта, имеющих разных наследников, и одинаково реализующие один и тот же интерфейс, функционал придется продублировать. Кроме дублирования есть проблема изменение версии интерфейса, которая затрагивает все реализации.

Есть множество хороших причин почему Java и C# используют модель классов/интерфейсов. Множественное наследование открывает многие двери, но происходит это за счет увеличения сложности и довольно неприятных нюансов. Fantom снова занимает середину, называемую примеси (mixins). Примеси — это интерфейсы, которую могут хранить в себе реализацию. Чтобы избежать ошибок множественного наследования, у примеси ограничены некоторые функции, такие как поля, хранящие состояние. Пример примеси:

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

Если интересно, java эквивалент можно посмотреть здесь.

Модульность

Модульность — важный аспект программного обеспечения, который необходим современному языку программирования.
К сожалению, последнюю декаду в java мы испытываем настоящий ад с classpath. Кроме проблем с classpath, java приняла неправильное решение и перешла к монолиту J2SE размером 44Mb, что значительно замедлило наши с вами приложения.
В .NET подошли к этому вопросу крайне серьезно, поэтому благодаря механизму версий, GAC и другим средствам часть проблем из Java была решена. Но они утеряли простоту zip модуля и начали паковать модули в DLL, которые содержат еще много разных запчастей, что не позволяет легко работать с модулем.

В Fantom все строится на модулях, называемых подами (pods). Как и в java, под — это просто zip файл, который можно легко посмотреть. Мета данные пода хранится в специальном файле /meta.props, представляющее собой записи вида ключ-значение, такие как pod.name, pod.version, pod.depends и так далее. Зависимости пода хранятся в его мета данных и описаны в явном и понятном виде.

Для организации кода в пространстве имен в java используются пакеты, а jar файл рассматривается как модуль. Несоответствие между этими понятиями вызывает большую проблему. У вас есть имя java класса, но оно вам не подскажет в каком jar файле живет этот класс и откуда его загружать.

В Fantom было принято простое решение для управления имен: три уровня иерархии в имени «pod::type.slot», т.е. на первом уровне всегда имя пода, затем имя типа (аналог Class в java) и затем имя слота (метод или поле). Такое согласованное поведение в пространстве имен позволяет легко управлять процессом сборки больших систем.

Функциональное программирование

Java и C# движутся в направлении полноценной поддержки замыканий, но после себя они оставляют большой след истории, в качестве старого API. Fantom создавался с поддержкой замыканий на начальной стадии. Замыкания — ключевая особенность языка, которая используется везде и всегда.

// 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 

Декларативное описание

Наилучшим описанием будет пара примеров:

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 }   ] } 

Параллелизм

Большинство языков в настоящий момент имеют одну общую модель между потоками. Это означает, что разработчик сам должен позаботиться о блокировке памяти. Некорректная блокировка может привести к неприятных ошибкам, таким как взаимные блокировки, состояние гонки и т.д. Все это довольно низкий уровень к управлению параллелизма.

Fantom поддерживает параллелизм, используя следующие техники:

  1. Неизменяемые объекты (потоковая безопасность)
    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 p.x = 10              // throws ConstErr  vowels := ['a','e','i','o','u'].toImmutable 

  2. Статические поля могут хранить только неизменяемые объекты, поэтому разные потоки не могут получить доступ к общим изменяемым данным.
  3. Модель сообщений (actors) для общения между потоками (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 

Синтаксический сахар

  1. Значения по умолчанию для параметров
    class Person {   Int yearsToRetirement(Int retire := 65) { return retire - age }   Int age } 

  2. Вывод типов — типы локальных переменных могут быть выводимыми
  3. Неявный доступ к полям
    class Thing {   Int id := 0   {     get { echo("get id"); return &id }     set { echo("set id"); &id = it }   } } 

  4. Нулевые типы — разделение типов на те, которые не могут принимать null, как значение, и которые могут.
    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. Убраны проверяемые исключения, в C# Anders Hejlsberg также не включил их (и правильно сделал)
  6. Числовая точность — существует только 64 разрядная поддержка Int и Float

Ресурсы

  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

Заключение

Распространённой ошибкой является мнение, что Fantom — это очередная улучшенная версия Java. Но стоит только взглянуть на него, как на язык со своей философией и концепциями, как начинаешь понимать всю его прелесть. Помимо стабильности, к Fantom можно отнести ряд таких особенностей, как дружелюбное сообщество, полностью открытый исходный код, приятную IDE и ещё множество приятных мелочей.

ссылка на оригинал статьи http://habrahabr.ru/post/175979/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *