Функциональное программирование 1 - функциональные типы

Попробую собрать лучшие идеи ФП, возникшие у меня при разработке Neo, в нескольких статьях.
Итак, часть 1 - функциональные типы.
Фунциональный тип - это тип, представляющий функцию. В функциональном программировани, как известно, функцию можно передать в другую функцию как аргумент и возвратить из функции как результат. Это значит, что для функций нужны специальные "функциональные типы" (ФТ). Рассмотрим историю ФТ на примере различных языков программирования.

Простейшим ФТ является указатель на функцию. Указатели на функцию существуют в языке Си. Их синтаксис таков:
int (*pfunc)(float f);
Внутри такого объявления описывается имя переменной (pfunc), тип возвращаемого значения и аргументы ФТ. В принципе, этого могло бы быть достаточно, так как таким способом можно передать любую функцию. Но во многих случаях это бывает неудобно. Собственно, удобство - как раз то, из-за чего и появлились языки высокого уровня всесто Ассемблера.

Часто вместе с функцией хочется передать некоторые аргументы этой фунцкии.
Наиболее частый случай - функция является методом класса. Любой нестатический метод имеет один неявный аргумент, традиционно называемый "this". Это указатель (ссылка) на объект того класса, от которого вызывается метод. Указатель на функцию - это чистый адрес функции, места для каких-либо аргументов там нет. Для того, чтобы передать вместе с указателем еще и аргумент, вводится понятие "делегат".

Термин "делегат" возник в C# (хотя там понятие немного расширено - в C# это список указателей на функции с аргументами this). Я считаю, что список - это список, а разработчики C# немного намудрили. В языке D, к примеру, делегат - это именно одиночное значение.

Формально, делегаты могут быть указателями на глобальные функции (если они поддерживаются ЯП), статические методы или на нестатические методы классов. То есть это структуры данных, имеющие поле - указатель на функцию, и поле для указателя this. Что интересно, в C++ Builder какой-то довольно старой версии появилось ключевое слово __closure, с помощью которого можно было создавать как раз такие объекты.

Но не всегда передачи одного this достаточно. В самом деле, чем указатель this отличается от других параметров? Что делать, если хочется сохранить вместе с указателем на функцию и часть обычных параметров этой функции? В функциональном программировании это называется "частичное применение": когда новая функция создается на основе существующей путем частичного указания аргументов. Самое интересное, что такое можно сделать и на старом добром Си, просто определив новую функцию, которая вызывает старую:

int Sum(int x, int y) { return x+y; }
int Inc(int x) { return Sum(x,1); }

Но интересный вопрос - почему-бы не сделать такое с помощью делегатов?
Если пользоваться терминологией C#/D, то такие объекты уже правильнее называть именно "функциональными объектами". Они действительно полноценные объекты ООП - имеют поля (указатель на функцию и поля, которые нужно передать в качестве аргументов этой функции). Теоретически, они могут иметь собственные методы, т.е. функциональные типы могут являться полноценными классами. Здесь они пересекаются с функторами - классами, у которых переопределен оператор "круглые скобки" (). За счет этого с такими объектами можно работать как с обычными функциями.

По сути, функторы и делегаты - разные проявления одного большого понятия "функциональные объекты", возможности которых выходят далеко за рамки простого объединения возможностей функторов и делегатов. Об этом далее.

No comments:

Post a Comment