Основы функционального программирования/Haskell/Модули и монады: различия между версиями

Содержимое удалено Содержимое добавлено
м Множество правок
м Замена Хаскель → Haskell
Строка 5:
Языки программирования не обходятся без понятия [[w:модуль (программирование)|модуля]], разве что за исключением языков самого [[w:Низкоуровневый язык программирования|низкого уровня]], да и те последнее время обретают некоторые [[w:Высокоуровневый язык программирования|высокоуровневые]] свойства. Модули появились, когда [[w:программирование|программирование]] только зарождалось и возникла надобность разбивать программы на логические части, каждую из которых создавал бы отдельный разработчик или коллектив.
 
В этой лекции освещается, как в [[w:Haskell|ХаскелеHaskell]] работают модули и более интересное новичкам явление [[w:Монада (математика)|монады]].
 
== Модули ==
 
В ХаскелеHaskell у модулей двоякое назначение: с одной стороны, — контроль за [[w:Пространство имён (программирование)|пространством имён]] (как и во всех других языках), с другой, — создание [[w:Абстрактный тип данных|абстрактных типов данных]].
 
Определить модуль просто: имя состоит из любых символов, лишь начинаться оно должно с прописной буквы. Имя модуля никак не связано с [[Файловая система|файловой системой]] (как, например, в [[w:Паскаль (язык программирования)|Паскале]] или [[w:Java|Java]]): файл, содержащий описание модуля, может называться отлично от самого модуля; также, он может содержать несколько модулей, ибо модуль есть лишь декларация самого высокого уровня.
 
На верхнем уровне модуля в ХаскелеHaskell может быть множество деклараций (описаний и определений): [[w:Тип данных|типы]], [[w:Класс (программирование)|классы]], [[w:Данные (вычислительная техника)|данные]], [[w:Функция (программирование)|функции]]. Один вид деклараций, если он вообще используется, должен стоять в модуле на первом месте: это включение в модуль других модулей, что делается словом <code>import</code>. Другие определения могут появляться в любой последовательности.
 
Определение модуля должно начинаться со служебного слова <code>module</code>. Ниже приведено определение модуля <code>Tree</code>:
Строка 38:
В приведенном примере видно, что модуль <code>Main</code> импортирует модуль <code>Tree</code>, причём в декларации <code>import</code> явно описано, какие именно объекты импортируются из модуля <code>Tree</code>. Если это описание опустить, то импортироваться будут все объекты, которые модуль экспортирует, то есть в данном случае можно было просто написать: <code>import Tree</code>.
 
В обычной практике один модуль импортирует несколько других, но при этом в импортируемых модулях есть объекты с одинаковым именем. Естественно, что в этом случае возникает конфликт имён. Чтобы этого избежать, в ХаскелеHaskell существует специальное служебное слово <code>qualified</code>, при помощи которого определяются те импортируемые модули, имена объектов в которых приобретают вид: <code>&lt;Имя Модуля&gt;.&lt;Имя Объекта&gt;</code>, то есть для того, чтобы обратиться к объекту из квалифицированного модуля, перед его именем необходимо написать имя модуля:
 
<code>module Main where
Строка 50:
=== Абстрактные типы ===
 
В ХаскелеHaskell модуль является единственным способом создать так называемые абстрактные типы данных, то есть такие, в которых скрыто представление типа, но открыты только специфические операции над созданным типом, набор которых вполне достаточен для работы с типом. Например, хотя тип <code>Tree</code> является достаточно простым, его всё-таки лучше сделать абстрактным типом, то есть скрыть то, что <code>Tree</code> состоит из <code>Leaf</code> и <code>Branch</code>. Это делается следующим образом:
 
<code>module TreeADT (Tree, leaf, branch, cell, left, right, isLeaf) where
Строка 69:
=== Другие аспекты использования модулей ===
 
Далее приводятся дополнительные аспекты системы модулей в ХаскелеHaskell:
 
* В декларации импорта (<code>import</code>) можно выборочно спрятать некоторые из экспортируемых объектов служебным словом <code>hiding</code>. Это бывает полезным для явного исключения определений некоторых объектов из импортируемого модуля.
Строка 79:
== Монады ==
 
Многие новички в [[w:Функциональное программирование|функциональном програмировании]] бывают часто озадачены понятием монады в ХаскелеHaskell. Но монады очень часто встречаются в языке: так, например, система ввода-вывода основана именно на понятии монады, а стандартные библиотеки содержат целые модули, посвящённые монадам. Необходимо отметить, что понятие монады в ХаскелеHaskell основано на [[w:Теория категорий|теории категорий]], но чтобы не вдаваться в абстрактную математику, далее будет представлено интуитивное понимание монад.
 
Монады суть типы, представляющие собой экземпляры одного из следующих монадических классов: <code>Functor</code>, <code>Monad</code> и <code>MonadPlus</code>. Ни один из этих классов не может быть предком для другого класса, то есть монадические классы ненаследуемы. В модуле <code>Prelude</code> определены три монады: <code>IO</code>, <code>[]</code> и <code>Maybe</code>, то есть список также является монадой.
Строка 96:
Точное значение операция связывания конечно же зависит от конкретной реализации монады. Так, например, тип <code>IO</code> определяет операцию <code>(&gt;&gt;=)</code> как последовательное выполнение двух её операндов, а результат выполнения первого операнда последовательно передаётся во второй. Для двух других встроенных монадических типов (списки и <code>Maybe</code>) эта операция определена как передача нуля или более [[w:Параметр (программирование)|параметров]] из одного вычислительного процесса в следующий.
 
В ХаскелеHaskell определено служебное слово, на уровне языка поддерживающее использование монад, — слово <code>do</code>, понимание которого можно увидеть в следующих правилах его применения:
 
<code>do e1 ; e2 = e1 &gt;&gt; e2
Строка 167:
fail :: String -&gt; m a</code>
 
Запись <code>m a</code> как бы показывает, что тип <code>a</code> (необходимо чётко помнить, что при определении классов и других типов данных символы типа <code>a</code>, <code>b</code> и так далее обозначают переменные типов) обрамлён монадическим типом <code>m</code>. Однако в реальности физическое обрамление доступно только для монадического типа «список», так как его обозначение в виде квадратных скобок пошло традиционно. В строгой нотации ХаскелаHaskell нужно было бы писать что-нибудь вроде: <code>List (1 2 3 4 5)</code> — это список <code>[1, 2, 3, 4, 5]</code>.
 
2. Применение монад в функциональных языках — это по существу возвращение к императивности. Ведь операции связывания <code>(&gt;&gt;=)</code> и <code>(&gt;&gt;)</code> предполагают последовательное выполнение связанных выражений с передачей или без результатов вычисления. То есть монады — это императивное ядро внутри функциональных языков. С одной стороны, это идёт в разрез с теорией функционального програмирования, где отрицается понятие императивности, но, с другой стороны, некоторые задачи решаются только при помощи императивных принципов. И опять же, ХаскелHaskell предоставляет удивительную возможность по генерации списков, но это только благодаря тому, что сам тип «список» выполнен в виде монады.