Полиморфизм: Применение функций к аргументам разных типов
Ad hoc polymorphism
Полиморфизм в программировании: ad hoc полиморфизм, перегрузка функций и операторов. Различия между ad hoc и параметрическим полиморфизмом. Объяснение и примеры.
Сравнивайте с английским: нажмите на абзац — оригинал откроется в окне. Кнопка EN под абзацем показывает его прямо в тексте.
Содержание
Введение
Применение полиморфных функций к аргументам разных типов
Applying polymorphic functions to arguments of different types
В языках программирования, ad hoc полиморфизм – это вид полиморфизма, при котором полиморфные функции могут применяться к аргументам различных типов, поскольку полиморфная функция может представлять собой ряд различных и потенциально разнородных реализаций в зависимости от типа аргумента (ов), к которым она применяется. Применительно к объектно-ориентированным или процедурным концепциям, он также известен как перегрузка функций или перегрузка операторов. Термин ad hoc в данном контексте не имеет негативного оттенка; он лишь указывает на то, что этот тип полиморфизма не является фундаментальной характеристикой системы типов. Это противопоставляется параметрическому полиморфизму, в котором полиморфные функции записываются без указания какого-либо конкретного типа и, таким образом, могут применять единую абстрактную реализацию к любому числу типов прозрачным образом. Эта классификация была предложена Кристофером Стречи в 1967 году.
In programming languages, ad hoc polymorphism is a kind of polymorphism in which polymorphic functions can be applied to arguments of different types, because a polymorphic function can denote a number of distinct and potentially heterogeneous implementations depending on the type of argument(s) to which it is applied. When applied to object oriented or procedural concepts, it is also known as function overloading or operator overloading. The term ad hoc in this context is not intended to be pejorative; it refers simply to the fact that this type of polymorphism is not a fundamental feature of the type system. This is in contrast to parametric polymorphism, in which polymorphic functions are written without mention of any specific type, and can thus apply a single abstract implementation to any number of types in a transparent way. This classification was introduced by Christopher Strachey in 1967.
Раннее обязательное действие
Ad hoc полиморфизм — это механизм диспетчеризации: управление, проходящее через одну именованную функцию, направляется в различные другие функции без необходимости указывать конкретную вызываемую функцию. Перегрузка позволяет определить несколько функций, принимающих разные типы, с одним и тем же именем; компилятор или интерпретатор автоматически обеспечивает вызов нужной функции. Таким образом, можно написать функции для добавления списков целых чисел, списков строк, списков чисел с плавающей точкой и т. д., и все они будут называться `append` — при этом будет вызываться правильная функция `append` в зависимости от типа добавляемых списков. Это отличается от параметрического полиморфизма, при котором функция должна быть написана обобщённо, чтобы работать с любым типом списка. Используя перегрузку, можно заставить функцию выполнять совершенно разные действия в зависимости от типа входных данных, переданных ей; это невозможно при параметрическом полиморфизме. Другой взгляд на перегрузку заключается в том, что процедура однозначно идентифицируется не только своим именем, но и комбинацией имени, количества, порядка и типов её параметров. Этот тип полиморфизма распространен в объектно-ориентированных языках программирования, многие из которых позволяют перегружать операторы аналогичным функциям (см. перегрузку операторов). Некоторые языки, которые не имеют динамической типизации и не поддерживают ad hoc полиморфизм (включая классы типов), используют более длинные имена функций, такие как `print_int`, `print_string` и т. д. Это можно рассматривать как преимущество (более описательно) или недостаток (чрезмерная многословность) в зависимости от точки зрения. Одним из преимуществ перегрузки является возможность специализации: функция с одним и тем же именем может быть реализована несколькими различными способами, каждый из которых оптимизирован для конкретных типов данных, с которыми она работает. Это может обеспечить удобный интерфейс для кода, который необходимо адаптировать к различным ситуациям для повышения производительности. Недостатком является то, что система типов не может гарантировать согласованность различных реализаций. Поскольку перегрузка выполняется во время компиляции, она не является заменой позднему связыванию, как это встречается в полиморфизме подтипов.
Ad hoc polymorphism is a dispatch mechanism: control moving through one named function is dispatched to various other functions without having to specify the exact function being called. Overloading allows multiple functions taking different types to be defined with the same name; the compiler or interpreter automatically ensures that the right function is called. This way, functions appending lists of integers, lists of strings, lists of real numbers, and so on could be written, and all be called append—and the right append function would be called based on the type of lists being appended. This differs from parametric polymorphism, in which the function would need to be written generically, to work with any kind of list. Using overloading, it is possible to have a function perform two completely different things based on the type of input passed to it; this is not possible with parametric polymorphism. Another way to look at overloading is that a routine is uniquely identified not by its name, but by the combination of its name and the number, order and types of its parameters. This type of polymorphism is common in object oriented programming languages, many of which allow operators to be overloaded in a manner similar to functions (see operator overloading). Some languages that are not dynamically typed and lack ad hoc polymorphism (including type classes) have longer function names such as print int, print string, etc. This can be seen as advantage (more descriptive) or a disadvantage (overly verbose) depending on one's point of view. An advantage that is sometimes gained from overloading is the appearance of specialization, e. g., a function with the same name can be implemented in multiple different ways, each optimized for the particular data types that it operates on. This can provide a convenient interface for code that needs to be specialized to multiple situations for performance reasons. The downside is that the type system cannot guarantee the consistency of the different implementations. Since overloading is done at compile time, it is not a substitute for late binding as found in subtyping polymorphism.
Позднейшее связывание
Несмотря на предыдущий раздел, существуют и другие способы реализации ad hoc полиморфизма. Рассмотрим, например, язык Smalltalk. В Smalltalk перегрузка выполняется во время выполнения, поскольку методы ("реализация функции") для каждого перегруженного сообщения ("перегруженная функция") разрешаются непосредственно перед выполнением. Это происходит во время выполнения, после компиляции программы. Таким образом, полиморфизм обеспечивается как полиморфизмом подтипов, как и в других языках, так и расширяется за счет ad hoc полиморфизма во время выполнения. Более внимательное рассмотрение покажет, что Smalltalk предоставляет несколько иную разновидность ad hoc полиморфизма. Поскольку Smalltalk использует модель исполнения с поздней привязкой, и поскольку объекты обладают способностью обрабатывать неподдерживаемые сообщения, можно реализовать функциональность, используя полиморфизм, без явной перегрузки конкретного сообщения. Хотя это может быть не самой рекомендуемой практикой для повседневного программирования, это может быть весьма полезно при реализации прокси. Кроме того, хотя перегрузка общих методов класса и конструкторов обычно не рассматривается как полиморфизм, существуют более единообразные языки, в которых классы являются полноценными объектами. Например, в Smalltalk классы – это обычные объекты. Это, в свою очередь, означает, что сообщения, отправляемые классам, могут быть перегружены, и можно создавать объекты, поведение которых имитирует классы, без наследования их классов от иерархии классов. Это эффективные приемы, позволяющие использовать мощные возможности рефлексии Smalltalk. Аналогичные возможности существуют и в языках Self и Newspeak.
The previous section notwithstanding, there are other ways in which ad hoc polymorphism can work out. Consider for example the Smalltalk language. In Smalltalk, the overloading is done at run time, as the methods ("function implementation") for each overloaded message ("overloaded function") are resolved when they are about to be executed. This happens at run time, after the program is compiled. Therefore, polymorphism is given by subtyping polymorphism as in other languages, and it is also extended in functionality by ad hoc polymorphism at run time. A closer look will also reveal that Smalltalk provides a slightly different variety of ad hoc polymorphism. Since Smalltalk has a late bound execution model, and since it provides objects the ability to handle messages that are not understood, it is possible to implement functionality using polymorphism without explicitly overloading a particular message. This may not be generally recommended practice for everyday programming, but it can be quite useful when implementing proxies. Also, while in general terms common class method and constructor overloading is not considered polymorphism, there are more uniform languages in which classes are regular objects. In Smalltalk, for instance, classes are regular objects. In turn, this means messages sent to classes can be overloaded, and it is also possible to create objects that behave like classes without their classes inheriting from the hierarchy of classes. These are effective techniques that can be used to take advantage of Smalltalk's powerful reflection capabilities. Similar arrangements are also possible in languages such as Self and Newspeak.