Путешествие из Node в Crystal

https://www.duodesign.co.uk/blog/from-node-to-crystal/
  • Перевод
В компании Duo много лет, в качестве основной платформы, использовали Node. Однако, в последнее время они экспериментировали с очень новым, ещё не вполне оформившимся языком Crystal. По их словам, чем больше они им занимались — тем сильнее к нему привязывались.

Сегодня мы хотим поделиться с вами переводом их рассказа о сильных и слабых сторонах платформ Node и Crystal, и о том, почему в Duo всё больше серверных проектов переводится на Crystal.



Node


▍Ожидания


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

Node тогда показался нам совершенно очевидным выбором. Если мы брали в штат разработчика, который знал JavaScript, это означало для нас, что он смог бы работать и на клиентских, и на серверных проектах. Инструменты, синтаксис и зависимости перекрывались бы, и все совершенствовали бы свои навыки, так как любая задача подразумевала бы использование JavaScript.

▍Реальность


У серверного и клиентского кода совершенно разные цели, эти виды кода требуют знания очень разных приёмов работы. Обычно клиентский код — это взаимодействие с пользователем, обновление интерфейса, выполнение запросов данных с сервера. Наши разработчики обычно работали с webpack или browserify для упаковки кода, разрабатывали интерфейсы на React и использовали CSS-фреймворки для упрощения разметки страниц.

На сервере программист имеет дело с SQL-запросами к базам данных, с ORM, с чтением и записью файлов, организует взаимодействие со сторонними API. Потоки данных на сервере подчиняются модели «запрос — ответ». Между запросами все задачи должны обслуживать ответы, и всё это нужно делать специфическим образом. Если некий шаг полагается на результаты, полученные на предыдущем шаге, соответствующие процессы должны выполняться по порядку, если нет — их можно выполнять параллельно.

▍Стандартная асинхронность


Node спроектирован так, что каждую задачу он выполняет асинхронно. Это означает, что если вы предложите Node выполнить 5 задач, он попытается сделать всё это одновременно. Несколько последних лет основным средством для поддержки такой модели работы были промисы. Если в двух словах, то промисы позволяют программисту объединять в цепочки наборы асинхронных задач, которые представляют собой последовательности шагов, выполняемых друг за другом.

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

За годы использования Node создание цепочек из промисов стало для нас нормой. Половина написанного кода была направлена на то, чтобы превратить асинхронные задачи в задачи, решаемые последовательно. Эти цепочки промисов тяжело тестировать, отлаживать, код получается не особенно вразумительным. Часто бывает так, что просто глядя на код тяжело понять даже то, в каком порядке выполняются задачи и подзадачи.

Роаль Даль, создатель Node, весьма удачно описал эту ситуацию, сравнивая Node и Go в интервью:

Но интерфейс, который эта система предоставляет программисту, блокирующий, и я думаю, на самом деле, что это — более удачная модель программирования. Использование блокирующего подхода позволяет, во многих ситуациях, гораздо лучше видеть суть выполняемых действий. Скажем, если есть куча последовательных действий, весьма полезна возможность сообщить системе примерно следующее: «Реши задачу А, подожди ответа, возможно — выдай ошибку. Реши задачу B, подожди ответа или выдай ошибку». И в Node, из-за необходимости постоянно «прыгать» между вызовами функций, достичь такого гораздо сложнее.

▍Динамические типы


Любой, кто регулярно программирует на JavaScript, рано или поздно познакомится с ошибкой «undefined is not an object». Эта ошибка возникает, когда вы пытаетесь обратиться к методу или свойству переменной, которую вы считаете объектом, но в которую записано значение null. Недостаточно контролировать то, какие данные передаются между асинхронными участками кода, необходимо ещё и быть в курсе того, что творится с типами в любом месте кода приложения. Каждый раз, когда приложение получает данные от одного процесса и передаёт их другому, может произойти сбой. Если вы не предусматриваете возможность обработки всех возможных значений, сервер выдаст ошибку, или, что гораздо хуже, сделает что-нибудь неожиданное.

Crystal


Во время работы с Node я исследовал множество других языков и платформ, в том числе — Python, PHP, Ruby и Go. Они, как правило, либо были медленнее, чем Node, либо не так удобны для целей разработки. Скорость и синтаксис — это две вещи в языке, которые можно оптимизировать лишь до определённого предела.

Затем, в прошлом году, я прочитал статью о языке Crystal. Он — из нового поколения языков, которые компилируются в машинный код через LLVM. Его синтаксис похож на Ruby (мне это нравится), но работает он так же быстро, как Go (а этому зверю скорости не занимать!).

Crystal всё ещё очень молод, но я решил переделать на нём некоторые серверные части нашей системы управления контентом. Получилось просто замечательно. Вот, что я выяснил в ходе работы:

  • Crystal отличается высокой производительностью. Для моих задач он оказался в 2 раза быстрее Node.
  • Он использует очень мало памяти. Crystal обычно надо менее чем 5 Мб на процесс, а Node — более 200 Мб.
  • У него имеется отличная стандартная библиотека, в результате для решения типичной задачи нам понадобилось лишь 12 зависимостей, в сравнении с сотней зависимостей Node.
  • Код, по умолчанию, выглядит синхронным, он использует, как и Node, цикл событий, но для организации параллелизма применяются легковесные потоки (fibers), взаимодействие организовано через каналы, как у Go. Это упрощает понимание кода.
  • Crystal статически типизирован, поэтому об ошибках можно узнать при компиляции.
  • Crystal выводит типы, в результате, его системой типов легко пользоваться, так как не приходится слишком часто использовать аннотации типов.

Мне так понравилось программировать на Crystal, что мы переписали весь бэкенд нашей CMS на этом языке. Его API совместимо с нашей CMS, основанной на Node, в результате вебсайты можно перевести на новую систему, или вернуть обратно на старую, затратив сравнительно мало усилий. Это важно, так как Crystal — всё ещё молодой проект и нам нужна страховка.

После того, как наша DuoCMS была полностью переписана на Crystal, мне понадобилось протестировать её в продакшне. Собственно говоря, оригинал этого материала размещён на сайте, который работает на Crystal.

▍Сравнение кода на Node и Crystal


Ниже, для сравнения, приведена слегка упрощённая версия кода контроллера, написанного на Crystal и Node.

Вот контроллер на Node (используется фреймворк Express).

const express = require('express')
const app = express()
const bodyParser = require('body-parser')
const UserService = require('user-service')
app.use(bodyParser.json())

app.get('/', function (req, res) {
  res.send('Hello World!')
})

app.post('/api/users', function (req, res) {
  if(request.body){
    UserService.save(request.body)
    .then(function(){
      res.send('user saved')
    })
    .catch(function(err){
      res.send(err)
    })
  }else{
    res.send("no user provided")
  }	
})

app.listen(3000, function () {
  console.log('Example app listening on port 3000!')
})

Вот — контроллер на Crystal (используется фреймворк Kemal).

require "kemal"
require "user"
require "user-service"

get "/" do
  "Hello World!"
end

post "/api/users" do |ctx|
  if (json = ctx.request.body)
    user = User.from_json(json)
    UserService.new.save(user)
    "user saved"
  else
    "no user provided"
  end
end

Kemal.run

Несложно заметить, что структура этих двух примеров очень похожа. Однако, когда отпала необходимость в промисах, общий объем кода уменьшился. При написании более крупных приложений это заметно ещё сильнее. Серверный код DuoCMS 5 состоит из примерно 15609 строк на JavaScript. Объём кода DuoCMS 6 близок к 10186 строкам. На данный момент DuoCMS 6 имеет больше возможностей, для реализации которых потребовалось на 30% меньше кода. При этом, благодаря отсутствию промисов, этот код гораздо легче читать и поддерживать.

Чего не хватает в Crystal?


Разработчики называют текущий релиз Crystal альфа-версией. Тут надо сказать, что мне приходилось использовать гораздо менее проработанные фреймворки, предназначенные для продакшна. В худшем случае я сказал бы, что Crystal сейчас в состоянии бета-версии. Однако, я могу понять осторожность разработчиков. Они говорят об альфа-версии, так как это даёт им пространство для манёвра, для внесения изменений, даже для того, чтобы поломать какое-нибудь API, и так далее.

Я использую Crystal уже примерно год и столкнулся лишь с немногими изменениями, которые объясняются развитием проекта и тем, что это — всё ещё альф-версия. У меня было больше проблем с обновлением React на фронтенде. Кроме того, стоит сказать, что Crystal написан на Crystal, то есть, если что-то окажется нерабочим, вы можете вносить исправления в язык и в стандартную библиотеку (я так и поступал).

На данный момент основными недостающими возможностями Crystal можно назвать следующие:

  • Всё ещё нет поддержки Windows (меня это не беспокоит, работаю я на Mac, код разворачиваю на Linux).
  • До сих пор нет настоящего параллелизма (в Node его тоже нет).
  • Нет инкрементной компиляции (это было бы очень удобно, так как сейчас, для компиляции нашей системы после внесения изменений в код, требуется около 8 секунд).
  • Существует не так много хорошо поддерживаемых опенсорсных библиотек для Crystal, но здесь всё придёт в порядок, когда начнётся использование Crystal в серьёзных проектах.

Для нас ни один из перечисленных недостатков не стал причиной отказаться от Crystal. Мне понадобилось внести несколько дополнений, реализующих отсутствующие возможности, в используемые нами библиотеки, но при работе с Node такое тоже случалось. В итоге могу сказать, что я весьма доволен Crystal.

Итоги


Стоит ли вам попробовать Crystal? Да, стоит! Штука это действительно замечательная. На Crystal приятно программировать, код просто читать и править. И, кстати, чем больше людей будет пользоваться Crystal и вносить вклад в разработку этого языка — тем лучше он будет становиться. Хотите увидеть всё своими глазами? Вот инструкции по установке. Вот — сайт проекта. А это — чат Crystal, если что — пишите мне на @crisward.

Если вы спросите — следует ли вам использовать Crystal в продакшне, ну — это как хотите. Лично я думаю, что единственный способ сделать что-либо пригодным к практической эксплуатации — попробовать это на тех задачах, на которых допустимы сбои, а потом постепенно переходить на это в более масштабных проектах. Мы не используем Crystal везде, например — на проектах с очень высоким трафиком, или на критически важных участках. Мы мониторим все наши сайты и регулярно их бэкапим, кроме того, Node всегда на подхвате — на тот случай, если с Crystal вдруг что-нибудь приключится.

Уважаемые читатели! Планируете ли вы попробовать Crystal в своих проектах?
RUVDS.com
1027,00
RUVDS – хостинг VDS/VPS серверов
Поделиться публикацией

Комментарии 56

    +15

    Эта статья будет не полной без самого популярного комментария из обсуждения на Reddit:


    Let me sum up.
    "We're JS twinks with no real experience with backend software dev, so instead of learning a new language that's well proven on the backend like Ruby or Python, we picked a JavaScript tool with almost no track record as a backend platform. Then we realized it sucks for a huge number of backend applications! So naturally…
    1) we picked yet another completely unproven, alpha quality platform… 2) that nobody else uses in production, so support is hard to come by when it breaks,… 3) with a custom syntax (even though we picked node instead of a good platform specifically to avoid learning a new syntax)… 4) AND WE REWROTE ALL OUR SHIT BECAUSE YOLO"
    Whoever makes tech decisions for this team is an irresponsible, incompetent idiot who has no business being near production systems.
      0

      Кому-то же нужно пробовать новые технологии. Очень хорошо, что кто-то потратил свое время и деньги, а не наше.

        0

        так всегда начинается :-) у меня один продакшн-сервис для работы с кредитными картами написан на Crystal, но он однозначно недоступен снаружи :)

        0
        Я бы советовал на D обратить внимание в сочетании с vibed.org
          +1

          Присоединяюсь.


          Crystal отличается высокой производительностью. Для моих задач он оказался в 2 раза быстрее Node.

          Да что угодно компилируемое быстрее ноды :-)


          Он использует очень мало памяти. Crystal обычно надо менее чем 5 Мб на процесс, а Node — более 200 Мб.

          Ну это как приложение напишешь.


          У него имеется отличная стандартная библиотека, в результате для решения типичной задачи нам понадобилось лишь 12 зависимостей, в сравнении с сотней зависимостей Node.

          На D возможно хватило бы и одной — vibe.d


          Код, по умолчанию, выглядит синхронным, он использует, как и Node, цикл событий, но для организации параллелизма применяются легковесные потоки (fibers), взаимодействие организовано через каналы, как у Go. Это упрощает понимание кода.

          В D аналогично, более того..


          До сих пор нет настоящего параллелизма (в Node его тоже нет).

          В D он мало того, что есть, так ещё некоторым контролем времени компиляции. Файберы могут баланситься на воркеры. Есть и реализация каналов как в го.


          Crystal статически типизирован, поэтому об ошибках можно узнать при компиляции.

          D тоже, кроме того имеет мощные возможности метапрограммирования. Например, шаблоны могут транслироваться в машинные коды при компиляции.


          Crystal выводит типы, в результате, его системой типов легко пользоваться, так как не приходится слишком часто использовать аннотации типов.

          D тоже их выводит, но более строго. Например, не пихает алгебраические типы где ни попадя, как ниже в комментах.


          Всё ещё нет поддержки Windows (меня это не беспокоит, работаю я на Mac, код разворачиваю на Linux).

          А меня беспокоит, ибо работаю под виндой. И D работает и под линуксом и под виндой. И тоже умеет компилироваться через LLVM.


          Нет инкрементной компиляции (это было бы очень удобно, так как сейчас, для компиляции нашей системы после внесения изменений в код, требуется около 8 секунд).

          А в D она есть, из-за чего компиляция идёт очень быстро. Настолько быстро, что на нём даже скрипты писать можно с компиляцией налету.


          Существует не так много хорошо поддерживаемых опенсорсных библиотек для Crystal, но здесь всё придёт в порядок, когда начнётся использование Crystal в серьёзных проектах.

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

            0

            Для D есть аналог Passport.js? Пока удалось найти только https://github.com/thaven/oauth, но хотелось бы стабильную версию, больше готовых провайдеров и хоть какую-то активность в репозитории.

              0
              Passport.js можно использовать как микросервис, без разницы на чем остальной бекенд.
                0

                Если я правильно понимаю, так сессию прийдётся хранить в редисе, нода будет создавать сессию, а в D можно будет подцеплять её, для моих задач пока хватает хранения в памяти или в куках, не хотелось бы усложнять.

                0

                Там последний коммит пару месяцев назад. Дополнительных провайдеров добавить вроде не сложно.

            –3
            Зачем переходить именно на Ruby Cristal, если есть много других более развитых платформ бэкэнда. Те же рельсы или Go.
            И чем вам Node не нравится
            Фактически на одном языке и фронтэнд и бэкэнд
              +1
              Написали же, именно это авторам оказалось не нужно.
              +3
              Эти цепочки промисов тяжело тестировать, отлаживать, код получается не особенно вразумительным. Часто бывает так, что просто глядя на код тяжело понять даже то, в каком порядке выполняются задачи и подзадачи.


              async/await через babel еще 2 года назад уже отлично работали
                0

                На Rust тоже можно писать веб приложения :)

                  0

                  мне Crystal нравится (потому что я рубист), но вся проблема в том, что язык еще не релизнулся: могут быть баги, обратно-несовместимые изменения и прочие прелести

                    0

                    гарантированно будут (я надеюсь).

                      0

                      А если взять какую-то ruby библиотеку и тупо компильнуть кристалом — взлетит? Или там такой игривый руби-лайк синтаксис, сейчас я руби, а сейчас нет? (я не рубист, если что)

                        +2

                        в 98% случаях не взлетит, потребует хотя бы рихтовки напильником.


                        Crystal просто основан на некоторых идеях из Ruby.


                        Но вообще это статически-типизируемый язык с type unions, редкая штука.


                        То есть тип Int32 — не может быть nilable, а Int32 | Nil может (другая запись — Int32?).


                        И это очень сильно и удобно (и да, можно Int64 | String, например, но вообще типы он сам выведет при компиляции, если явно не писать).

                          0
                          Int64 | String — это же обычные дженерики, нет?
                            0

                            Нет, метод может вернуть type union Int64 | String.


                            При этом по факту у вас есть две автоматически созданные ветви исполнения по типу возвращаемого результата.


                            def iors : Int64 | String
                              Math.rand > 0.5 ? 5 : "s"
                            end
                            
                            r = iors
                            if r.is_a?(String)
                              puts "wow #{r}"
                            end

                            здесь для разных типов будут созданы/скомпилированы разные ветви исполнения, так что потеря времени компиляции только на определение типа возвращаемого значения (одна проверка только один раз после получения результата, если это необходимо)

                              0

                              более корректно код писать как:


                              def iors
                                Random.rand > 0.5 ? 5 : "s"
                              end
                              
                              r = iors
                              if r.is_a?(String)
                                puts "wow #{r}"
                              end
                              0
                              Наверное тут имеются в виду алгебраические типы данных. То есть, например, метод может вернуть или int32 или string, а потом в зависимости от того, что по факту вернулось (с помощью if или какого pattern matching) будет своя ветка исполнения.
                              Ещё один пример — функция возщает или ответ или ошибку
                              0

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


                              def iors : Int64 | (puts "Welcome to hell" > Nil ? String : Nil)
                                  Math.rand > 0.5 ? 5 : "s"
                              end
                          +4
                          Несложно заметить, что структура этих двух примеров очень похожа. Однако, когда отпала необходимость в промисах, общий объем кода уменьшился. При написании более крупных приложений это заметно ещё сильнее. Серверный код DuoCMS 5 состоит из примерно 15609 строк на JavaScript. Объём кода DuoCMS 6 близок к 10186 строкам. На данный момент DuoCMS 6 имеет больше возможностей, для реализации которых потребовалось на 30% меньше кода. При этом, благодаря отсутствию промисов, этот код гораздо легче читать и поддерживать.


                          Можно и порефакторить этот js и получим тоже — читаемый код и уменьшение кодовой базы
                          const router  = require('express').Router();
                          
                          app.get('/', (req, res) => res.send('Hello World!'));
                          
                          app.post('/api/users', async (req, res) => {
                            const {body} = request;
                            if (!body) {
                              return res.send("no user provided")
                            }
                            
                            await UserService.save(request.body);
                            res.send('user saved');
                          })
                            +2
                            К тому же на crystal нет обработки ошибки сохранения пользователя — замечательное сравнение.
                              0

                              Скорее всего, обработка исключений происходит внутри фреймворка (как в sinatra), так что всё ок

                                0
                                Так какая разница, где оно обрабатывается? Как я узнаю, что произошла нештатная ситуация? Наружу ничего не передается и, судя по всему, даже не кидается исключение.
                                  0

                                  Исключение может кидаться внутри сервиса, создающего юзеров или в модели. Так как js-версия ничего не делает, кроме как выводит ошибку в браузер, код примерно равнозначен: тут это исключение обработает фреймворк, отдав корректный 500-ый статус (чего, кстати, не сделает js-вариант)

                              +1

                              Ну на деле Kemal, — это просто первая ласточка, сейчас есть и иные каркасы, типа https://ambercr.io


                              Во-вторых, да, async/await — прекрасная концепция.


                              А в остальном, — Crystal надежнее и удобнее в силу своей статической типизации, тут его ближайший конкурент все-таки — tsnode Golang.

                              0

                              Я — активный фанат кристала и мне больно смотреть, как в него приходят js'еры и рубисты и начинают буквально ср*ть в опенсорс, ни разу не используя преимущества языка. Они думают, что они всё ещё в динамическом ЯП.


                              Я против кемала и amber, потому что кристал самодостаточен, и можно быстро написать быстрое приложение, используя только стандартную библиотеку. Я в принципе против фреймворков.


                              А ещё автор забыл упомянуть макросы. Метапрограммирование есть и в кристале, и это одна из самых главных его особенностей.

                                +2

                                Напишите статью )

                                  +2
                                  А почему тогда не nim? Явно быстрее crystal, есть то же самое метапрограммирование через макросы и прочее.
                                    0
                                    К тому же там как раз планируется большое кол-во изменений в отношении к GC — деструкторы, которым не нужен GC (в компилятор добавился новый проход «destroyer», в котором в код будут вставляться вызовы деструкторов в нужных местах), к тому же строки и последовательности станут свободными от GC (уже есть эксперименты с этим).
                                    В IRC/Gitter можете пообщаться на эту тему с Araq (создателем языка).
                                      +1
                                      Меня вымораживает отсутствие неймспейсов (из-за универсального способа вызова методов) — в интеллисенсе миллион функций может быть при импорте из нескольких внешних библиотек. Тут даже go-lang поприятнее как-то, но в нем нет исключений. :(
                                        +1
                                        Можно сделать импорты как в питоне:
                                        from sequtils import nil
                                        echo sequtils.repeat('h', 15)
                                        
                                          0

                                          Не миллион, не преувеличивайте, они же фильтруются по типу первого аргумента.

                                            0
                                            Это если вызывать одним способом (как контекстно-зависимый метод), а если вызывать наоборот (как функцию с контекстом в виде первого параметра) — тогда все возможные функции-методы будут одним скопом.
                                      0
                                      > А ещё автор забыл упомянуть макросы. Метапрограммирование есть и в кристале, и это одна из самых главных его особенностей.

                                      Макросы на строках, без гигиены, в негомоиконном языке — это не метапрограммирование, это курам на смех.
                                        0

                                        Ну как раз в Crystal макросы — на AST.


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

                                          0
                                          > Ну как раз в Crystal макросы — на AST.

                                          В презентации сказано, что АСТ — только промежуточное представление и в итоге из него генерятся строки.
                                            0

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

                                              0
                                              И толку, если там в середине были строки? Нормальную макросистему на этом изобразить нельзя, в строках просто нету требуемой информации.
                                      0

                                      Если вам по душе ruby-like синтаксис, то почему тогда не Elixir?


                                      Блин, перевод же

                                        +1

                                        Я думаю проблема в том что автор пытался на ноде кодить синхронно.


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


                                        class UserController {
                                          requests(router) { // router уже держит /api 
                                            router.post('/users', this.handleRequest(this.addUser))
                                          }
                                        
                                          addUser(req) {
                                            // вообще у нас прикручен валидатор на запросы, но ладно уж
                                            if (!req.body) throw new ServerError("no user provided"); // addUser исполняется в промисе, так что ошибка вывалится в catch
                                        
                                            return UserService.save(req.body)
                                                .then(() => 'user saved');
                                          }
                                        }
                                          0
                                          Они, как правило, либо были медленнее, чем Node, либо не так удобны для целей разработки.

                                          Интересно, python медленее nodejs (если все-таки использовать асинхронность) или менее удобен?)


                                          А так crystal наконец-то похож на язык, который как python по гибкости, только компилируется. Надо будет глянуть.

                                            0

                                            пока желательно только для инфраструктуры внутренней.


                                            наружу раньше февраля не стоит.

                                              0
                                              а что планируется в феврале?
                                                +1

                                                Жесткого плана нет (есть карта).


                                                Ранее была амбициозная цель к 2018 году создать Crystal 1.0, готовый к промышленной эксплуатации.


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


                                                Мне вот не хватает только иногопоточности для некоторых применений.

                                            +2
                                            Мне так понравилось программировать на Crystal, что мы переписали весь бэкенд нашей CMS на этом языке.

                                            Я правильно понял, что автор только-только изучил новый язык и сразу принялся на нём писать в прод? Крайне сомнительная и не профессиональная затея. Возможно, стоило отрефакторить старый код, убрать лишние зависимости. Уверен, это дало бы прирост CPU / памяти.

                                              +1
                                              Я как рубист на кристал давно засматриваюсь. Попробовать очень хочу, но пока никак.
                                                –1
                                                Crystal
                                                Почему не Nim или Julia?
                                                  0
                                                  Потому что Nim очень сырой и в нем хаотично надерганы концепции из кучи других языков.
                                                  Julia все же больше для математиков и ученых создана.
                                                    0
                                                    Nim уже не очень сырой, багов всё меньше, и все фичи в языке смотрятся с друг другом нормально (например дженерики с концептами)
                                                  +1
                                                  называйте меня как хотите, но сравнивать языки со скобками и языки с «end» это…
                                                    0

                                                    блоки в Crystal можно оформлять и в фигурных скобках, это принято для однострочников.


                                                    5.times { |i| puts i }
                                                    
                                                    yo = -> { puts "yo" }
                                                    yo.call
                                                      0
                                                      ужас — кроме фигурных скобок в строке могут быть только некоторые другие специальные символы, иначе теряеться вся суть.
                                                      yo = -> { puts "yo" } 


                                                      зачем это? проще уже без сколбок
                                                        0

                                                        увы, ужас непонят. все читаемо.

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

                                                  Самое читаемое