В языках программирования ad hoc полиморфизм [1] является разновидностью полиморфизма , при котором полиморфные функции могут применяться к аргументам разных типов, поскольку полиморфная функция может обозначать ряд различных и потенциально неоднородных реализаций в зависимости от типа аргумента(ов), к которому она применяется. При применении к объектно-ориентированным или процедурным концепциям это также известно как перегрузка функций или перегрузка операторов . Термин ad hoc в этом контексте не предназначен для уничижительного восприятия; он просто относится к тому факту, что этот тип полиморфизма не является фундаментальной особенностью системы типов . Это контрастирует с параметрическим полиморфизмом , при котором полиморфные функции записываются без упоминания какого-либо конкретного типа и, таким образом, могут применять одну абстрактную реализацию к любому количеству типов прозрачным образом. Эта классификация была введена Кристофером Стрейчи в 1967 году.
Полиморфизм ad hoc — это механизм диспетчеризации : управление, проходящее через одну именованную функцию, передается различным другим функциям без необходимости указывать точную вызываемую функцию. Перегрузка позволяет определять несколько функций, принимающих разные типы, с одним и тем же именем; компилятор или интерпретатор автоматически гарантирует, что будет вызвана правильная функция. Таким образом, функции, добавляющие списки целых чисел , списки строк , списки действительных чисел и т. д., могут быть написаны, и все они будут называться append — и правильная функция append будет вызываться на основе типа добавляемых списков. Это отличается от параметрического полиморфизма, в котором функция должна быть написана обобщенно , чтобы работать с любым видом списка. Используя перегрузку, можно заставить функцию выполнять две совершенно разные вещи на основе типа переданных ей входных данных; это невозможно при параметрическом полиморфизме (но должно быть достигнуто путем переключения типа внутри обобщенной функции). Другой способ взглянуть на перегрузку заключается в том, что процедура однозначно идентифицируется не своим именем, а комбинацией своего имени и количества, порядка и типов своих параметров.
Этот тип полиморфизма распространен в объектно-ориентированных языках программирования, многие из которых позволяют перегружать операторы способом, аналогичным функциям (см. перегрузка операторов ). Некоторые языки, которые не являются динамически типизированными и не имеют полиморфизма ad hoc (включая классы типов), имеют более длинные имена функций, такие как print_int
, print_string
и т. д. Это можно рассматривать как преимущество (более описательно) или недостаток (излишне многословно) в зависимости от точки зрения.
Преимущество, которое иногда достигается перегрузкой, — это появление специализации, например, функция с одним и тем же именем может быть реализована несколькими различными способами, каждый из которых оптимизирован для конкретных типов данных, с которыми она работает. Это может обеспечить удобный интерфейс для кода, который необходимо специализировать для нескольких ситуаций по соображениям производительности. Недостатком является то, что система типов не может гарантировать согласованность различных реализаций.
Поскольку перегрузка выполняется во время компиляции, она не заменяет позднее связывание , как в полиморфизме подтипов .
Несмотря на предыдущий раздел, существуют и другие способы, которыми может работать ad hoc полиморфизм. Рассмотрим, например, язык Smalltalk. В Smalltalk перегрузка выполняется во время выполнения, поскольку методы («реализация функции») для каждого перегруженного сообщения («перегруженная функция») разрешаются, когда они собираются выполняться. Это происходит во время выполнения, после компиляции программы. Таким образом, полиморфизм задается путем подтипирования полиморфизма, как в других языках, и он также расширяется по функциональности посредством ad hoc полиморфизма во время выполнения.
Более пристальный взгляд также покажет, что Smalltalk предоставляет немного иную разновидность ad hoc полиморфизма. Поскольку Smalltalk имеет модель выполнения с поздним связыванием и поскольку он предоставляет объектам возможность обрабатывать сообщения, которые не поняты, можно реализовать функциональность с использованием полиморфизма без явной перегрузки конкретного сообщения. Это может быть не совсем рекомендуемой практикой для повседневного программирования, но может быть весьма полезно при реализации прокси.
Кроме того, хотя в общих чертах перегрузка методов и конструкторов общих классов не считается полиморфизмом, существуют более однородные языки, в которых классы являются обычными объектами. Например, в Smalltalk классы являются обычными объектами. В свою очередь, это означает, что сообщения, отправляемые классам, могут быть перегружены, и также возможно создавать объекты, которые ведут себя как классы, без наследования их классов от иерархии классов. Это эффективные методы, которые можно использовать для использования мощных возможностей отражения Smalltalk . Аналогичные договоренности также возможны в таких языках, как Self и Newspeak .
Представьте себе оператор +
, который можно использовать следующими способами:
1 + 2 = 3
3.14 + 0.0015 = 3.1415
1 + 3.7 = 4.7
[1, 2, 3] + [4, 5, 6] = [1, 2, 3, 4, 5, 6]
[true, false] + [false, true] = [true, false, false, true]
"bab" + "oon" = "baboon"
Для обработки этих шести вызовов функций необходимы четыре различных фрагмента кода (или три, если строки рассматривать как списки символов):
Таким образом, название +
фактически относится к трем или четырем совершенно разным функциям. Это пример перегрузки или, точнее, перегрузки операторов .
Обратите внимание на неоднозначность в строковых типах, используемых в последнем случае. Рассмотрим "123" + "456"
, в котором программист мог бы естественным образом предположить сложение, а не конкатенацию. Они могут ожидать "579"
вместо "123456"
. Перегрузка может, таким образом, предоставить различное значение или семантику для операции, а также различные реализации.