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, ось деякі корисні ресурси для початку роботи (англ.):
- Crystal Play – онлайн REPL-подібний інструмент для Crystal
- Install Crystal
- Crystal for Rubyists
- Crystal Exercisms
- Create your own HTTP Server in minutes
Ще немає коментарів