
- •Лекция 5. Ламбда-исчисление как формализация языка функционального программирования
- •5.2. Аксиомы ламбда-исчисления
- •5.2.1. Конвертируемость
- •6.1. Понятие комбинатора
- •6.2. Комбинаторная логика как формальная система
- •6.2.1. Правила конструирования допустимых для заданного алфавита комбинаторных выражений (термов).
- •6.2.2. Аксиомы комбинаторной логики
- •6.2.3.Умолчания
- •6.2.4. Синтаксис выражений комбинаторной логики
- •6.2.5. Редукция комбинаторных термов
- •6.2.6. Базис термов
- •7.1. Определение типов
- •7.2. Аксиомы вывода типа комбинатора
- •7.3. Типизация выражений в sml
- •7.3.1. Схема типизации
- •7.3.2. Выводимость типов
- •7.3.3. Полиморфная типизация
- •7.4. Преимущества типизации
- •7.5. Применение типизации
- •7.5.1. Статическая типизация
- •7.5.2. Полимофная типизация
- •7.6. Управление типами в технологии .Net
7.3.3. Полиморфная типизация
Еще одной важной позитивной особенностью языка программирования SML является то обстоятельство, что в нем поддерживается так называемая полиморфная типизация, суть которой можно объяснить на основе следующего примера.
Рассмотрим функцию обработки списка, которая упорядочивает его элементы по возрастанию. В классическом языке программирования со строгой типизацией, например, в языке Pascal, неизбежно придется реализовать, по крайней мере, две функции: для случаев числовых и строковых элементов списка. В SML такой необходимости не возникает, т.к. существует возможность описания функции обработки списка с переменным типом аргументов, которая безошибочно обработает и список из чисел, и список из строк, существенно сэкономив трудозатраты.
Проиллюстрируем выводимость типов в языке программирования SML на примере.
Рассмотрим функцию
let
val Id = fn x => x
in (Id 3, Id true)
end
С точки зрения анализатора корректности типизации эта функция является корректно типизированной (well-typed). Конструкция let, по сути, представляет собой подстановку одной функции в другую. В этой связи выводимость типов иначе называется let-полиморфизмом.
Рассмотрим другую функцию
fn Id => (Id 3, Id true)) (fn x => x)
В отличие от предыдущей, данная функция является некорректно типизированной (ill-typed), поскольку однозначно определить тип параметра x, в отличие от предыдущего примера, не представляется возможным.
7.4. Преимущества типизации
Рассмотрев введение в математическую теорию типов и подходы к типизации в языках программирования, представим в концентрированном виде те преимущества, которые отличают языки программирования и формальные теории с типами.
Прежде всего, отметим то бесспорное преимущество типизированных исчислений, что при таком подходе моделируемая предметная область структурирована лучше, чем в том случае, если отсутствует сегментация на типы. Типизация структурирует предметную область по иерархическому принципу.
Введение типизации облегчает и упорядочивает не только восприятие, но и управление предметной областью. Манипулирование типизированными элементами носит более целенаправленный характер, причем появляется возможность обрабатывать разнородные сущности предметной области различным образом, а однородные (или, точнее, однотипные) - единообразно.
7.5. Применение типизации
Перейдем к языкам программирования и практике проектирования и реализации программных систем. В случае построения языка программирования по принципу строгой типизации несоответствия типов фиксируются до начала этапа выполнения программы (на этапе контроля соответствия типов в ходе трансляции), что гарантирует отсутствие семантических (смысловых) и логических ошибок и безопасность программного кода.
Проиллюстрируем на примерах механизм работы процедуры, осуществляющей вывод типов в языке программирования SML.
7.5.1. Статическая типизация
Пример 1. Определим тип статической переменной x, которой присвоим значение, сводимое к целому типу:
val x=2*3;
val x = 6 : int
Как и следовало ожидать, значение переменной x оказывается целочисленным (int).
Заметим, что функция val, которая используется в примере, является стандартной функцией языка программирования SML для определения типа языкового объекта, т.е. фактически осуществляет приписывание типа (при необходимости используя механизм выводимости типов).
Пример 2. Определим тип константы, которой присвоим значение, также сводимое к целому типу:
1+2;
3 : int
Как и следовало ожидать, значение константы 3 также оказывается целочисленным (int).
Пример 3. Определим тип функции сложения двух целочисленных аргументов:
fun add (x : int)(y : int) = x+y;
val add = fn : int -> int -> int
Как видно из результата, типом функции является функция из пары целочисленных значений в целочисленное значение (с точностью до скобок).
Пример 4. Проиллюстрируем означивание (вычисление значения) определенной выше функции сложения двух целочисленных аргументов:
add 1 3;
val it = 4 : int
Очевидно, что, как и следует из ранее вычисленного типа
(int -> int -> int), функция, принимая на вход пару целочисленных величин, возвращает значение целочисленного типа.
Пример 5. Рассмотрим тип функции, которая является производной от функции add и выполняет операцию прибавления единицы:
val f = add 1;
val f = fn : int -> int
Вновь введенная функция имеет тип "из целого числа в целое число" (int -> int) .
Наконец, означивание последней функции
f 4;
val it = 5 : int
в соответствии с ее типом дает целочисленный результат.