Введение


DISCLAIMER

мнение автора может не совпадать с вашим мнением, добро пожаловать в комментарии.


В доисторические времена высокопроизводительные Web-приложения можно было писать, в основном, на C или C++. Поддерживать такие приложения было не просто дорого, а очень дорого.


Потребность в среде программирования, в которой удобно писать, читать и изменять высокопроизводительные приложения, появилась давно.


В первую очередь термины "производительность" и "надежность" относятся к Erlang. В своей нише он великолепен, но синтаксис заставляет желать лучшего. Собственно, именно поэтому появился Elixir, но речь сейчас не об этой экосистеме.


Если же немного снизить планку надежности, то здесь перед нами широкий выбор, включая Node, Go, Nim и Crystal. Можно взглянуть на типичные сравнительные данные по производительности, включая более обширные.


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


При это Node предлагает всем понятный язык программирования (и вариации на тему), но динамическая типизация снижает производительность в несколько раз относительно других претендентов.


Если нам нужно больше запросов в секунду, то выбор на сегодня — Go. Эта среда программирования обладает великолепными характеристиками производительности, поддержку со стороны крупных компаний и немалое число активных проектов.


Так почему же Crystal?


LLVM


Crystal предоставляет фронтэнд для инфраструктуры LLVM, позволяя компилировать код под все поддерживаемые платформы (x64, ARM, Web Assembly etc.).


Статическая типизация без указания типов


Crystal способен в большинстве случаев сам вывести типы используемых данных.


Например, мы где-то отправляем в метод speak в одном случае строку, в другом случае целое число.


В таком случае Crystal на этапе компиляции знает, что метод speak принимает параметр выведенного типа String | Int32.


Мы можем написать такой код:


speak(15)
speak("Whatever")

def speak( thing )

  thing.say_every # Ошибка компиляции, у переменной thing тип (Int32 или String), а у него нет метода say_every

  # Здесь можно пользоваться методами класса Object, а также всех классов, общих предков String и Int32
  if thing.is_a?(String)
    # здесь Crystal гарантирует нам, что у переменной thing тип String, и можно пользоваться его методами.
    return thing.sub(/\Athe\s+/, "")
  elsif thing.responds_to?(:to_s)
    # Здесь Crystal гарантирует нам, что у thing есть метод to_s, и его можно вызвать 
    return thing.to_s
  end
end

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


Проверка на пустое значение


В общем случае специальная проверка в коде на пустое значение не требуется. Например, в этом примере попытка передачи пустого значения приведет к ошибке компиляции, так как my_string имеет тип (String или Nil), а у типа Nil нет метода  upcase:


if rand(2) > 0
  my_string = "hello world"
end

puts my_string.upcase

Модель параллелизма


Crystal поддерживает зеленые потоки, каналы, и опцию SO_REUSEPORT — аналогично Go. В настоящее время идёт работа над качественной поддержкой многопоточности на уровне операционной системы, что приведет к эффективным приложениям на нескольких процессорных ядрах.


Библиотеки


В Crystal фактически встроен свой менеджер зависимостей — Shards.


Функциональность аналогична ruby bundler, perl carton etc. При этом зависимости описываются в shards.yml проекта.


BDD, общий интерфейс по работе с РСУБД и прочие приятности включены в стандартную библиотеку.


Я не стану упоминать различные библиотеки для анализа данных и прочая, для этого есть список Awesome Crystal.


Из того, что не входит в стандартную поставку, но полезно для web-приложений:


Kemal


require "kemal"

# Matches GET "http://host:port/"
get "/" do
  "Hello World!"
end

# Creates a WebSocket handler.
# Matches "ws://host:port/socket"
ws "/socket" do |socket|
  socket.send "Hello from Kemal!"
end

Kemal.run

crystal-pg


PG_DB.query_one("select ARRAY[1, null, 3]", &.read(Array(Int32?))
# => [1, nil, 3]

PG_DB.query_one("select '{hello, world}'::text[]", &.read(Array(String))
# => ["hello", "world"]

Remarkdown


require "remarkdown"

Remarkdown.to_html("Hello **world**")

Замечания


  • Надо сказать, что изучил Crystal я на этих выходных, успев выпустить один проект (благодаря сходству синтаксиса с Ruby). И эта статья является точкой зрения новичка в мире Crystal.


  • Язык пока в процессе реализации, так что большие проекты пока на нём делать не стоит. Версия 1.0 планируется в конце года, но это неточно.


  • Пример простой библиотеки: https://github.com/akzhan/luhn.cr.
Поделиться с друзьями
-->

Комментарии (18)


  1. Alexeyco
    10.05.2017 18:58
    +2

    Если же немного снизить планку надежности, то здесь перед нами выбор из Node, Go и Crystal.
    Erlang весь надежный, node, go, crystal чуть похуже. Java, C# вообще никак не были упомянуты. Видимо, чтобы до них дошло, нужно, чтобы планка производительности куда-нибудь совсем потерялась. (сарказм)

    Какие критерии надежности применялись для оценки? Чем надежность Java или C# или Python не устроила? Нет, правда, я уважаю Erlang. Но конкретика же.


    1. Alexeyco
      10.05.2017 19:09
      +3

      Летом 1889 года Антон Палыч Чехов беседовал с неким И. Я. Гурляндом и заявил: «Если вы в первом акте повесили на сцену пистолет, то в последнем он должен выстрелить. Иначе — не вешайте его.» Вот мне интересно, Erlang-то тут к чему был? Так просто?


      1. akzhan
        10.05.2017 19:40

        Пока у меня нет готовых проектов на elixir :-)


        1. JC_IIB
          11.05.2017 06:03
          +1

          А чем конкретно не нравится синтаксис Erlang?
          В незапамятные времена человек, учивший меня писать на VoiceXML, сказал отличную фразу — «Тут главное правильно вывернуть мозг». Так и с Erlang — как только мозг выворачивается, внутри что-то щелкает и синтаксис Erlang перестает казаться чем-то необычным. И спустя какое-то время думаешь — хм, а переменные-то и впрямь не нужны. Ох, и без циклов можно обойтись. А еще тут какие-то свертки есть… ну и все такое.
          По поводу Elixir я лучше ничего не буду говорить :)


    1. akzhan
      10.05.2017 19:38

      Да, C# и Java здесь не затрагивались, мое упущение. Отдельные интересные экосистемы.


      1. Alexeyco
        10.05.2017 21:19

        Да просто ничем не подкрепленные утверждения. Что Go — самый быстрый. Автор (то есть, вы) заявляет, но никак ни на что не ссылается. Предполагается, что все написанное — просто догма.


        1. akzhan
          10.05.2017 21:26

          В тексте статьи изначально ссылка на одно из сравнений языков по производительности, позднее добавил еще две.


          1. Alexeyco
            11.05.2017 12:25

            Надежность. Почему node, go, nim, crystal — супернадежные, а C# и Java даже не были удостоены? Мне нравится пить эту чашу. Ответы-то будут или ограничитесь только минусами?


            1. akzhan
              11.05.2017 13:49

              В C# и Java нет супервизоров на уровне VM.


    1. akzhan
      10.05.2017 19:47

      На самом деле в статье многое упущено, например, nim. Немного прояснит ситуацию https://github.com/kostya/benchmarks


  1. zolern
    10.05.2017 20:27
    +1

    Интересные времена настали: резвые новые языки тягаются с дедушками C и C++ и выигрывают. Вот к примеру Crystal (Ruby на стероидах). Или мой любимый Nim (а-ля Python на стероидах) — https://nim-lang.org. Кстати интересно будет сравнение Crystal и Nim (Nim тоже существено быстрее Go).


    Как показываеть сравнение CSV Game (https://bitbucket.org/ewanhiggs/csv-game) Nim шустр почти как C, a вот Crystal-я там почему-то нет :)


    1. akzhan
      11.05.2017 19:00

      Можно, думаю, считать похожими по производительности.


      • Nim — новый статически типизированный язык для LLVM со стороны Python.


      • Crystal — новый статически типизированный язык для LLVM со стороны Ruby.

      Немного в стороне


      • Kotlin — новый статически типизированный язык для JVM со стороны Scala etc.?.


  1. Ph-s
    10.05.2017 20:28
    +3

    Язык пусть и не стабильный на сей момент (alpha всё таки), но лично у меня вызывает только восторг. И по моим личным ощущениям, он ну очень близок к руби.
    Принципиальные отличия от руби — типы всё-таки часто приходится указывать явно, что в случае с кортежами, хешами — порой вынуждает искать другие, более удобные для этого случая подходы. хотя приведение типов имеет и пока не реализованный потенциал.
    И вместо метапрограммирования как такового, тут компайл-тайм макросы.

    Еще менее стабильны сейчас библиотеки (shards), ибо каждый апдейт языка приносит пачку breaking changes.
    Обещают остановиться делать столько ломающих изменений когда достигнут 1.0.
    К 1.0 так же торопятся сделать concurency, сейчас же в полной мере реализованы только корутины (fibers) и каналы.
    + достаточно богатая стандартная библиотека.

    + шарды стараются не отставать. даже развиваются фреймворки а-ля синатра (kemal), а-ля рельсы (ametist), + нечто среднее (kemalist, lol). последние два не пробовал.


    1. mehatron
      10.05.2017 23:58

      (kemalist, lol)

      В какой-то момент подумал, что lol — это название фреймворка. Удивился и даже полез гуглить)


  1. ElectroGuard
    11.05.2017 10:26
    -1

    Нативный код впереди всей планеты.
    Возможно будет кому-то интересно. Веб приложения на Delphi:
    unigui

    image
    image
    image


  1. egordeev
    11.05.2017 11:02

    на swift не пробовали писать? Есть фреймворки vapor, kitura, perfect.
    есть ещё rust, фреймворк iron


    1. Ph-s
      11.05.2017 11:17

      Rust все-таки своеобразен и малость хардкорен для веба)


      1. egordeev
        11.05.2017 11:54
        +1

        rust своеобразен и по сути эту проблему можно решить, если дополнить офф. документацию этого языка бОльшим количеством примеров(особенно сравнительных примеров с другими я.п.);
        либо просто можно поискать проекты уже готовые