5 причин для рубістів використовувати Crystal

6 хв. читання

1. Дуже низька крива навчання

Подумайте про мови програмування, які стали популярними за останні 5-10 років. Що спадає на думку? Elixir, Go чи Rust? Всі вони продуктивніші за Ruby, але їх складніше освоїти.

Що, якщо ви можете отримати приріст продуктивності з набагато простішою кривою навчання?

Наскільки простішою? Подивімося на деякий код.

Питання: Який з наступних модулів написаний на Ruby? А який на Crystal?

module Year
  def self.leap?(year)
    year % 400 == 0 || (year % 100 != 0 && year % 4 == 0)
  end
end
module Hamming
  def self.distance(a,b)
    a.chars.zip(b.chars).count{|first, second| first != second }
  end
end

Відповідь: Питання з підступом – обидва. Модулі вище будуть працювати й в Ruby, і в Crystal.

Дивіться більше прикладів подібності коду тут.

Це не означає, що весь код Ruby буде працювати в Crystal (і навпаки).

Як Crystal (суворо типізована та компільована) діє як Ruby (динамічна та неявно типізована)? Компілятор Crystal використовує комбінацію двох потужних методів: вивід типів та типи об'єднань. Це дозволяє компілятору читати ruby-подібний код та з'ясовувати (виводити) правильні типи для використання.

Крім схожості, Crystal пропонує ряд переваг над Ruby, про які річ піде далі.

2. Compile-time перевірка та перевантаження методів

Чи не здається вам хаком те, коли ви пишете is_a? або respond_to?, щоб упевнитись, що ваш код в Ruby не зламався? Ви коли-небудь турбувалися про всі місця, де ви НЕ додали ці перевірки? Чи є це все багами сповільненої дії?

Crystal – компільована мова, яка перевіряє під час компіляції всі входи та виходи вашого методу. Якщо які-небудь типи не збігаються, вони будуть відловлені до виконання.

Повернімося до вище наведеного прикладу Year::leap?. Що трапляється у Ruby, коли значення не є цілим числом?

Year.leap?("2016") #=> false
Year.leap?(Date.new(2016, 1, 1)) #=> undefined method `%' for #<Date: 2016-01-01 ... >

Для String ми отримуємо невірну відповідь, для Date – виключення під час виконання. Для виправлення подібних речей у Ruby потрібне хоча б одне твердження is_a?:

module Year
  def self.leap?(input)
    if input.is_a? Integer
      input % 400 == 0 || (input % 100 != 0 && input % 4 == 0)
    elsif input.is_a? Date
      input.leap?
    else
      raise ArgumentError.new("must pass an Integer or Date.")
    end
  end
end

Чи підходить вам цей метод? У нас все ще є шанс отримати виключення під час виконання, просто з більш інформативним повідомленням про помилку.

В Crystal у нас є можливість явно вказувати тип наших входів (і виходів). Ми можемо змінити сигнатуру методу на self.leap? (year : Int) і ми гарантовано отримаємо ціле число в якості вхідних даних.

Ми отримуємо корисні повідомлення під час компіляції, а не під час виконання:

Year.leap?("2016")
Error in line 10: no overload matches 'Year.leap?' with type String
Overloads are:
 - Year.leap?(year : Int)

Якщо нам хочеться додати підтримку Time (DateTime у Ruby) в наш модуль, ми можемо перевантажити Year::leap?:

module Year
  def self.leap?(year : Int)
    year % 400 == 0 || (year % 100 != 0 && year % 4 == 0)
  end

  def self.leap?(time : Time)
    self.leap?(time.year)
  end
end

Як і в Ruby, перевантаження методів дозволяє гнучко використовувати вхідні дані, але без вгадування неявної типізації. Compile-time перевірка запобігає виникненню помилок несумісності типів.

3. Надшвидка продуктивність

Іншої перевагою компільованості є швидкість та оптимізація. Порівнюючи продуктивність Ruby з Crystal, різницю часто можна вказувати скоріше в порядках, ніж у відсотках.

В одному прикладі додавання випадкових чисел у crystal може бути на 10 порядків швидке, ніж у Ruby (приблизно на 37 мільйонів відсотків швидше). Причина – оптимізація компілятора й можливість використовувати примітиви в Crystal. Але це також призводить до ризику цілочисленного переповнення для великих чисел (див. пояснення).

Вбудований HTTP-сервер Crystal зміг обробити понад 2 мільйони запитів на секунду в тесті продуктивності. І багато веб-фреймворків послідовно забезпечують для веб-застосунків час відповіді у мілісекундах.

4. Веб-фрейморк, який ви хочете, вже тут

Любите повноту Rails (або навіть Phoenix від Elixir)? Ви будете відчувати себе як вдома з фреймворком Amber.

Чи вам більше подобається простота й легке налаштування Sinatra? Ви знайдете цю простоту разом з Kemal.

Або ви хочете full-stack фреймворк, що використовує перевірку часу компіляції для сильних параметрів, HTTP-дієслів та запитів до БД? Тоді спробуйте Lucky.

5. Crystal написаний на Crystal! Легко зрозуміти й внести свій вклад у розвиток мови

Ви коли-небудь читали вихідний код Ruby? Намагалися з'ясувати, як працюють деякі методи Enumerable?

Ruby реалізація Enumerable#all?

static VALUE
enum_all(int argc, VALUE *argv, VALUE obj)
{
    struct MEMO *memo = MEMO_ENUM_NEW(Qtrue);
    rb_block_call(obj, id_each, 0, 0, ENUMFUNC(all), (VALUE)memo);
    return memo->v1;

Скільки часу знадобиться, щоб з'ясувати, що робить цей код? Якщо ви ніколи не працювали с кодом С, вірогідно дуже багато часу.

Порівняйте з Crystal реалізацією Enumerable#all?

def all?
  each { |e| return false unless yield e }
  true
end

А скільки часу знадобиться на з'ясування тут? Якщо ви знаєте Ruby або Crystal, це займе кілька секунд.

З огляду на це, вважайте, що 98.4% Crystal написано на Crystal, і тільки 0.3% – на С++.

Ruby на 30.6% написаний на C та на 64.8% на Ruby.

Фактично, тільки один файл в crystal написаний на С++, отже, поки ви не змінюєте розширення LLVM, що б ви не шукали у мові Crystal, воно практично гарантовано було написане на Crystal.

Читання, розуміння та внесення змін до Crystal простіше, ніж у будь-якій мові, яку ви можете знайти.

З чого почати?

Якщо ви хочете спробувати мову програмування Crystal, ось деякі корисні ресурси для початку роботи (англ.):

Помітили помилку? Повідомте автору, для цього достатньо виділити текст з помилкою та натиснути Ctrl+Enter
Codeguida 5.5K
Приєднався: 8 місяців тому
Коментарі (0)

    Ще немає коментарів

Щоб залишити коментар необхідно авторизуватися.

Вхід / Реєстрація