Синтаксис объявления функций

Говоря о функциональном программировании, невозможно не рассмотреть основы ФП - функции, их представление в виде кода.
С точки зрения дизайна языка, существует два классических способа объявления функции: си-подобный и функциональный.



1. классический Си-подобный:


возвращаемый_тип имя_функции(список_аргументов)


Такой способ достаточно компактен, более чем привычен (C, C++, Java, C#), но в плане дизайна не лишен некоторых недостатков. И хотя в простейших случаях они могут не проявляться, следующий способ в любом случае открывает больше возможностей.



2. классический функциональный


ключевое_слово имя_функции (список_аргументов) возвращаемые_значения



Способ применяется в PHP, JavaScript, Python, Ruby, Go, Rust, Scala, Nemerle. Даже в C++11 подобный способ появился как альтернатива традиционному определению функций (и там это вызвано вполне прагматическими причинами). Практически во всех современных языках разработчики выбирают именно этот способ, и это не случайно.
Особенностью этого способа является то, что объявление функции начинается с ключевого слова. Это удобно для различных парсеров, особенно для IDE. Кроме того, это удобно для программиста - как минимум, проще искать функции в коде. Такой способ позволяет унифицировать внешнйи вид объявления функций с другими объявлениями - переменных, структур, перечислений, классов и т.д. В случае, если функция шаблонная, вполне логично и иногда даже необходимо, чтобы возвращаемый тип был определен после (и на основе) принимаемых (это причина ввода нового синтаксиса в С++).


Кроме того, появляется возможность определить несколько ключевых слов для определения разных типов функций (особенно это касается методов классов).


Несмотря на все это, во многих языках объявление функций способом 2 получается более громоздким, чем "сишный" вариант. Это связано с тем, что 1) выбирают слишком длинные ключевые слова (function, procedure), и 2) вводят много "мусорного" синтаксиса (всякие двоеточия, стрелочки и т.п.).


Таким образом, каждый метод предваряется некоторым ключевым словом:
def — для впервые объявленных функций 
redef — для переопределенных (в дочерних классах)
virtual - для виртуальных
override - для переопределенных виртуальных


Кроме того, по аналогичным причинам имеет смысл использовать ключевые слова для конструкторов и деструкторов:
init - конструкторы
final — деструкторы



Никаких декоративных элементов (двоеточий, стрелок) в определении функции не используется. Также можно сократить длину за счет отказа от указания типа для каждого аргумента. Если несколько перечисляемых один за другим аргументов имеют один и тот же тип, то достаточно указать этот тип один раз в самом начале.


def Func2(int x, y) char
{
}



Дополнительно следует сказать про имена конструкторов и деструкторов. Если любой метод обязан иметь имя, то конструкторы и деструкторы - методы init и final могут как иметь произвольные имена, так и не иметь их. Имена, если они есть, подчиняются общим правилам именования методов. Опциональная возможность именования конструкторов и деструкторов дает дополнительную возможность самодокументирования кода. Варинат с именами:


init InitDefault() {}
init InitFromStr(string s) {}


или по-старинке:



init() {}
init(string s) {}


 если имя есть, то на функцию-конструктор можно сослаться, можно вызывать ее для переинициализации объекта, взять ее адрес, можно вызвать один конструктор из другого и т.д. Дополнительные преимущества такого подхода - имя класса освобождается от дополнительной нагрузки (в C++ и C# имена конструкторов и деструкторов совпадают с именем класса). Если предположить, что мы имеем дело с языком, в котором каждая программная сущность — объект, то снимается неоднозначность: имя класса — уникальный идентификатор объекта класса, имя конструктора — уникальный идентификатор объекта-функции.


No comments:

Post a Comment