Модули группируют методы и классы, реализующие соответствующие функциональные возможности. Например, модуль Random из стандартной библиотеки содержит методы генерации всех видов случайных значений. Класс может быть "привязан" к одному или нескольким модулям; тогда они образуют так называемый \mixin\. Таким образом, объекты класса могут использовать методы модуля.
Давайте сделаем модуль под названием Hardness, который содержит метод hardness,
возвращающий эту характеристику для данного минерала:
===
foun...
...
\фрагмент кода изъят для повышения уникальности, сверяйтесь с книгой-оригиналом\
===
В этом примере наш класс Mineral теперь имеет лишь одно свойство — name, но зато включает в себя "полезные ресурсы" модуля Hardness:
===
foundations/module...
...
\фрагмент кода отсутствует...\
===
Включив этот модуль, вы сможете вызывать его методы для любого объекта Mineral:
===
fou...
...
===
Класс также может расширять (extend) модуль, и тогда его методы становятся для класса "своими".
___
[Примечание:
Наглядно объяснить различия extend и include можно так:
*Расширение — класс вытягивается и накрывает пространство модуля; происходит "обобщение ресурсов" модуля и класса.
*Включение — методы модуля будут добавлены к личному "арсеналу" всех создаваемых экземпляров класса, для которого прописано include... ; Короче, это дополнение собственного содержимого.]
Хотя и трудно продемонстрировать их с реальной пользой в таком простом примере, вы должны узнать о Волокнах (или Нитях) в языке Crystal, прежде чем идти дальше. Они могут изменить ваш подход к проектированию ваших программ. В этот век многоядерных микропроцессоров и распределенных вычислений, разработчикам ПО нужно, чтобы их языки программирования обеспечивали великолепную поддержку многопоточности и параллельной обработки данных.
К сожалению, это то, в чем Руби никогда не был очень хорош. В Ruby вы можете создать множество потоков выполнения с помощью Thread.new, но они работают на
уровне операционной системы. Их можно получить максимум несколько сотен. Кроме того, в его Си-реализации, язык Ruby ограничен GIL (глобальная блокировка интерпретатора), которая подразумевает, что только один поток Ruby может работать единовременно, так что будет задействовано лишь одно ядро.
В противоположность ему, язык программирования Crystal изначально разработан с учетом поддержки параллельных синхронных вычислений; хотя во время написания оригинального (англ.) руководства — реализация многопоточности все еще была в активной разработке.
Модель конвейерного "разложения" нагрузки в Crystal базируется на двух концепциях:
- Волокна, которые представляют собой легкие нити, созданные методом spawn и контролируемые непосредственно средой выполнения Crystal.
- Каналы, по которым волокна взаимодействуют друг с другом.
Основная программная нить — волокно, но другие волокна, которые она порождает, будут работать на заднем плане, не мешая первичной. Каналы должны знать, данные какого типа проходят через них, и потому каналы приходится типизировать.
Надо сказать, что язык Ruby с версии 1.9 тоже поддерживает волокна, но язык программирования Crystal, в отличие от Ruby, сам занимается распределением волокон, не требуя внимания программиста. Вот простой пример, создающий канал для транспортировки строк. В цикле, повторяющемся 10 000 раз, мы порождаем волокно и говорим ему отправить строку "fiber #: I like crystals!" через канал, и так мы знаем, какое именно волокно отправило его. Затем первичное волокно получает эту строку и записывает ее сообщение в стандартный вывод:
===
__fo...
===
На практике, конечно, здесь будет выведено гораздо больше, чем в приведенном примере.
Обратите внимание на важное различие между "синхронным" и "параллельным" в Crystal:
“concurrent” означает, что в одном потоке одновременное запущено несколько волокон (и выполнение
идет на одном процессорном ядре), их называют "сопрограммы" (т. е. группа подпрограмм) во многих других языках программирования.
Параллельный (parallel), с другой стороны, означает, что одновременно выполняются две или более программные ветви, каждая на отдельном ядре (или обособленном процессоре). До версии 0.26.1 исходный код Crystal выполнялся в одном потоке. Это значит, что все волокна выполняются синхронно, и Crystal делает это хорошо и быстро, но тогда он еще не мог работать параллельно на нескольких ядрах. Тем не менее, команда разработчиков активно работала над тем, чтобы в ближайшем будущем язык стал "параллельно разделяемым".
Относительно этой темы мы обсудим ещё многое в разделе "Создание распределяемого (параллельного) кода".
ВАШ ВЫХОД 7 (Практическое задание 7).
Используя вышеприведенный пример, узнайте время, необходимое для создания 500 000 волокон.
Заключение
Используя несколько простых примеров на основе минералов, мы исследовали базовые конструкции и синтаксис языка Crystal. Вы познакомились с основными типами переменных, вы научились повторять выполнение кода и управлять программным потоком, и вы изучили некоторые "множественные" типы, такие как массивы и хэши. Вы также научились с методами, классами и модулями, и пример на ЯП Ruby вы преобразовали в исходный код для Crystal. Мы остановились на простом примере с использованием волокон. По пути вам следует увидеть, как компилятор направляет программиста к более надежному и безопасному коду.
В части II вы получите гораздо больше подробностей по всем этим темам, и посмотрите, как язык программирования Crystal комбинирует собственные оригинальные решения, чтобы подчеркнуть свой акцент на производительность.
Другое