- •1. Поколения языков программирования.
- •2.Понятие фп, история развития.
- •1 . Трактовка функции через понятие переменная
- •2. Определения без переменных:
- •3.Программирование при помощи функций.
- •4. Программирование при помощи процедур.
- •5. Символьные данные в строго-функциональных языках.
- •6.Элементарные селекторы, конструкторы и предикаты.
- •7. Рекурсивные функции.
- •8. Проблема выбора подфункции
- •9. Накапливающий параметр — аккумулятор
- •10. Локальное определение.
- •11. Функции высших порядков.
- •12. Фвп применительно к языку Haskell.
- •13. Основы лямбда исчисления
- •14. Правило преобразования лямбда выражения.
- •15. Ромбическое свойство системы редукций.
- •16. Стандартные порядки редукций.
- •18. Структуры данных и их типы на языке Haskell.
- •1. Синонимы типов
- •19. Понятие модуля в Haskell.
- •Абстрактные типы данных
- •Другие аспекты использования модулей
- •20. Классы и их экземпляры в Haskell.
- •21. Наследование в языке Haskell.
- •22. Сорта типов и структуры данных.
- •[Править]Определение
- •[Править]Примеры
Абстрактные типы данных
В Haskell'е модуль является единственным способом создать так называемые абстрактные типы данных, т.е. такие, в которых скрыто представление типа, но открыты только специфические операции над созданным типом, набор которых вполне достаточен для работы с типом. Например, хотя тип Tree является достаточно простым, его все-таки лучше сделать абстрактным типом, т.е. скрыть то, что Tree состоит из Leaf и Branch. Это делается следующим образом:
module TreeADT (Tree, leaf, branch, cell, left, right, isLeaf) where
data Tree a = Leaf a
| Branch (Tree a) (Tree a)
leaf = Leaf
branch = Branch
cell (Leaf a) = a
left (Branch l r) = l
right (Branch l r) = r
isLeaf (Leaf _) = True
isLeaf _ = False
Видно, что к внутренностям типа Tree внешний пользователь (программист) может пробраться только при помощи использования определённых функций.
Впоследствии, когда создатель этого модуля захочет изменить представление типа (например, оптимизировать его), ему необходимо будет изменить и функции, которые оперируют полями типа Tree. В свою очередь программист, который использовал в своей программе тип Tree, ничего менять не будет, т.к. его программа все так же останется работоспособной.
Другие аспекты использования модулей
Далее приводятся дополнительные аспекты системы модулей в Haskell'е:
В декларации импорта (import) можно выборочно спрятать некоторые из экспортируемых объектов (при помощи служебного слова hiding). Это бывает полезным для явного исключения определений некоторых объектов из импортируемого модуля.
При импорте можно определить псевдоним модуля для квалификации имен экспортируемых из него объектов. Для этого используется служебное слово as. Это может быть полезным для того укорачивания имен модулей.
Все программы неявно импортируют модуль Prelude. Если сделать явный импорт этого модуля, то в его декларации возможно скрыть некоторые объекты, чтобы впоследствии их переопределить.
Все декларации instance неявно экспортируются и импортируются всеми модулями.
Методы классов могут быть так же как и подтипы данных перечислены в скобках после имени соответствующего класса во время декларации экспорта/импорта.
20. Классы и их экземпляры в Haskell.
Под полиморфизмом в объектно-ориентированной парадигме программирования понимается способность программы автоматически выбирать правильный метод (функцию) для использования в зависимости от типа данных, полученных для обработки. При этом на этапе трансляции исходных кодов тип данных может быть и вовсе неизвестен (не определен).
Парадигма функционального программирования поддерживает чистый, или параметрический, полиморфизм. Однако большинство современных языков программирования высокого уровня не обходятся без так называемого полиморфизма «ad hoc», или перегрузки имен функций и прочих объектов. Так, к примеру, перегрузка имен практически повсеместно используется для следующих
целей.
1. Литералы 1, 2, 3 и т. д. (то есть цифры) используются как для записи целых
чисел, так и для записи чисел произвольной точности.
2. Арифметические операции (например, сложение — знак (+)) используются для работы с данными различных типов (в том числе и с нечисловыми данными, например конкатенация для строк).
3. Оператор сравнения (в языке Haskell знак двойного равенства — (==)) используется для сравнения данных различных типов.
Множества — целые, действительные и мнимые числа — это различные типы данных.
Каждый тип данных требует своего собственного представления в первую очередь в памяти компьютера. Ну и, естественно, при визуализации значений этого типа для пользователя могут использоваться средства, специфические для каждого типа данных в отдельности. Поэтому использование одних и тех же средств является полиморфизмом «ad hoc».
element:: a -> [a] -> Bool
element _[]= False
element x (y:ys) = if (x==y) then True
else element x ys
(==):: a -> a -> Bool
Поскольку переменная типа a может обозначать любой тип (в том числе и совершенно несравниваемый, определенный пользователей), целесообразно определить операцию (==) таким образом, чтобы она могла быть используема для сравнения таких значений, для которых сама операция сравнения вполне осмысленна.
В общих словах для сравнения величин типа a требуется отдельная функция. Однако все эти функции хотелось бы называть одним и тем же именем, а именно (==), так как это вполне удобно.
Невозможно (да и бессмысленно) определять функцию (==) для любого типа данных, даже еще не разработанного. Для решения этой задачи в языке Haskell используется понятие класса типов. При использовании классов типов можно ассоциировать функцию (==) с определенным классом, а любой тип, входящий в этот класс, будет просто обязан реализовать данную функцию.
class Eq a where
(==), (/=) :: a -> a -> Bool
Haskell поддерживает и такую незаурядную вещь, как параметрический, или истинный, полиморфизм. И помогает в этом деле только что рассмотренный полиморфизм «ad hoc».
Haskell поддерживает статическую модель типизации, при использовании которой необходимо, чтобы тип каждого объекта, участвующего в вычислительном процессе, был известен до стадии выполнения. Система классов типов, которая успешно позволяет указать, какие операции (функции) можно использовать над данными того или иного типа.
Данная технология позволяет создавать синонимы имен функций, которые используются для тех или иных целей в рамках класса, то есть являются реализациями методов класса. Сама функция, может иметь любое имя, но при помощи связывания этого имени с наименованием метода класса и происходит реализация перегрузки имен методов.
Это непосредственно ведет к чистому полиморфизму, когда ни разработчик, ни транслятор языка Haskell уже не задумываются над тем, как устроен вычислительный процесс. Программист просто использует перегруженные имена методов, а транслятор просто вызывает связанную с ними функцию.
element :: Eq a => a -> [a] -> Bool
…
Данная запись показывает, что функция element может получить на вход любое значение и любой список значений типа a, такой и только такой, какой поддерживает операцию сравнения (==). На это указывает директива Eq a. в данном случае виден пример истинного полиморфизма, когда в наличии имеется одна-единственная функция, которая может получить на вход значения, подчи-
няющиеся указанным ограничениям.
Таким образом, разработанная в языке Haskell модель типизации включает в себя два типа полиморфизма — перегрузку имен и параметрический (истинный) полиморфизм, когда одна-единственная функция обслуживает данные различных типов. Эти два вида полиморфизма неразрывно связаны друг с другом, и параметрический полиморфизм основан на полиморфизме «ad hoc».
Сравнение с другими языками.
Классы, используемые в Haskell, похожи на классы в других объектно-ориентированных языках, таких как C++ и Java. Однако существуют некоторые важные отличия:
-Haskell отделяет определение типа от определения методов, связанных с этим типом. Класс в C++ или Java обычно определяет и структуру данных (переменные – члены класса) и функции, ассоциированные с этой структурой (методы). В Haskell эти определения даются раздельно.
-Метод класса, определённый в классе Haskell, соответствует виртуальной функции в классе языка C++. Каждое воплощение класса обеспечивает своё собственное определение для каждого метода, умолчания в классе соответствуют реализации по умолчанию виртуальной функции в базовом классе C++.
-Классы Haskell в грубом приближении похожи на интерфейсы в Java. Как и объявления интерфейсов, объявления классов в Haskell задают протокол использования объектов, а не определение собственно объекта.
-Haskell не поддерживает перегрузку в стиле C++, при которой функции с различными типами разделяют общее имя.
-Для типов объектов в Haskell нет неявного приведения; отсутствует универсальный базовый класс, вроде Object, к которому можно приводить значения.
-C++ и Java присоединяют идентифицирующую информацию (такую, как таблица виртуальных функций) к представлению объекта во время исполнения. В Haskell такая информация присоединяется к значениям логически, а не физически, через систему типов.
-В систему классов Haskell не встроен контроль доступа (такой, как метки public или private в определении класса). Вместо этого для сокрытия или раскрытия компонентов следует пользоваться системой модулей.
Класс Real
Описание: этот класс покрывает все числовые типы, элементы которых могут быть представлены как отношения (типичный пример — рациональные числа).
Определение класса Real, приведенное в стандартном модуле Prelude:
class (Num a, Ord a) => Real a where
toRational :: a -> Rational
Экземпляры: Int, Integer, Float, Double.
Класс Num
Описание: это родительский класс для всех числовых классов. Любой экземпляр этого класса должен поддерживать элементарные арифметические операции (такие как сложение, вычитание и умножение).
Определение класса Num, приведенное в стандартном модуле Prelude:
class (Eq a, Show a) => Num a where
(+), (-), (*) :: a -> a -> a
negate :: a -> a
abs, signum :: a -> a
fromInteger :: Integer -> a
fromInt :: Int -> a
