В математике и информатике каррирование — это метод перевода функции , принимающей несколько аргументов , в последовательность семейств функций, каждое из которых принимает один аргумент.
В прототипическом примере все начинается с функции , которая принимает два аргумента, один из и один из , и создает объекты в Каррированная форма этой функции обрабатывает первый аргумент как параметр, чтобы создать семейство функций Семейство организовано таким образом, что для каждого объекта в существует ровно одна функция
В этом примере, сам становится функцией, которая принимает в качестве аргумента и возвращает функцию, которая сопоставляет каждый с Правильная нотация для выражения этого многословна. Функция принадлежит к набору функций Между тем, принадлежит к набору функций Таким образом, что-то, что сопоставляется с, будет иметь тип С этой нотацией, является функцией, которая берет объекты из первого набора и возвращает объекты во втором наборе, и поэтому можно записать Это несколько неформальный пример; более точные определения того, что подразумевается под «объектом» и «функцией», приведены ниже. Эти определения различаются от контекста к контексту и принимают разные формы в зависимости от теории, в которой вы работаете.
Каррирование связано с частичным применением , но не является им . [1] [2] Пример выше можно использовать для иллюстрации частичного применения; он довольно похож. Частичное применение — это функция , которая принимает пару и вместе в качестве аргументов и возвращает Используя ту же нотацию, что и выше, частичное применение имеет сигнатуру Записанное таким образом, применение можно рассматривать как сопряженное к каррированию.
Каррирование функции с более чем двумя аргументами можно определить по индукции.
Каррирование полезно как в практических, так и в теоретических условиях. В функциональных языках программирования и многих других оно обеспечивает способ автоматического управления тем, как аргументы передаются функциям и исключениям. В теоретической информатике оно обеспечивает способ изучения функций с несколькими аргументами в более простых теоретических моделях, которые предоставляют только один аргумент. Наиболее общая установка для строгого понятия каррирования и декаррирования находится в закрытых моноидальных категориях , которые лежат в основе обширного обобщения соответствия Карри–Ховарда доказательств и программ до соответствия со многими другими структурами, включая квантовую механику, кобордизмы и теорию струн. [3]
Концепция каррирования была введена Готтлобом Фреге [4] [ 5], развита Моисеем Шёнфинкелем [6] [ 5] [7] [8] [9] [10] [11] и далее развита Хаскеллом Карри [8] [10] [12] [13]
Декаррирование — это двойственное преобразование к каррированию, и его можно рассматривать как форму дефункционализации . Оно берет функцию , возвращаемое значение которой — другая функция , и выдает новую функцию , которая принимает в качестве параметров аргументы для обоих и , и возвращает в качестве результата применение и впоследствии, , к этим аргументам. Процесс можно повторять.
Каррирование предоставляет способ работы с функциями, принимающими несколько аргументов, и использования их в фреймворках, где функции могут принимать только один аргумент. Например, некоторые аналитические методы могут применяться только к функциям с одним аргументом. Практические функции часто принимают больше аргументов, чем это. Фреге показал, что достаточно предоставить решения для случая с одним аргументом, поскольку вместо этого можно преобразовать функцию с несколькими аргументами в цепочку функций с одним аргументом. Это преобразование — процесс, который теперь известен как каррирование. [14] Все «обычные» функции, которые обычно встречаются в математическом анализе или в компьютерном программировании, могут быть каррированы. Однако существуют категории, в которых каррирование невозможно; наиболее общие категории, которые допускают каррирование, — это замкнутые моноидальные категории .
Некоторые языки программирования почти всегда используют каррированные функции для получения нескольких аргументов; яркими примерами являются ML и Haskell , где в обоих случаях все функции имеют ровно один аргумент. Это свойство унаследовано от лямбда-исчисления , где многоаргументные функции обычно представлены в каррированной форме.
Каррирование связано с частичным применением , но не является им . [1] [2] На практике программная техника замыканий может использоваться для выполнения частичного применения и своего рода каррирования, скрывая аргументы в среде, которая перемещается вместе с каррированной функцией.
«Карри» в «Каррирование» — отсылка к логику Хаскеллу Карри , который широко использовал эту концепцию, но Моисей Шёнфинкель придумал эту идею за шесть лет до Карри. [10] Было предложено альтернативное название «Шёнфинкелизация». [15] В математическом контексте этот принцип можно проследить до работы Фреге 1893 года . [4] [5]
Неясно, кто придумал слово «каррирование». Дэвид Тернер утверждает, что слово было придумано Кристофером Стрейчи в его лекциях 1967 года « Основные концепции языков программирования » [16], но этот источник вводит понятие как «устройство, придуманное Шёнфинкелем», и термин «каррирование» не используется, в то время как Карри упоминается позже в контексте функций высшего порядка. [7] Джон К. Рейнольдс определил «каррирование» в статье 1972 года, но не утверждал, что придумал этот термин. [8]
Каррирование проще всего понять, начав с неформального определения, которое затем можно будет сформулировать так, чтобы оно подходило для многих различных областей. Во-первых, необходимо установить некоторую нотацию. Нотация обозначает все функции от до . Если — такая функция, мы пишем . Пусть обозначают упорядоченные пары элементов и соответственно, то есть декартово произведение и . Здесь и могут быть множествами, или они могут быть типами, или они могут быть другими видами объектов, как будет рассмотрено ниже.
Дана функция
каррирование создает новую функцию
То есть принимает аргумент типа и возвращает функцию типа . Он определяется как
для типа и типа . Затем мы также пишем
Декаррирование — это обратное преобразование, и его легче всего понять в терминах его правого сопряженного — функции
В теории множеств для обозначения набора функций из множества в множество используется нотация . Каррирование — это естественная биекция между набором функций из в и набором функций из в набором функций из в . В символах:
Действительно, именно эта естественная биекция оправдывает экспоненциальную запись для множества функций. Как и во всех случаях каррирования, приведенная выше формула описывает сопряженную пару функторов : для каждого фиксированного множества функтор является левым сопряженным к функтору .
В категории множеств объект называется экспоненциальным объектом .
В теории функциональных пространств , например, в функциональном анализе или теории гомотопий , обычно интересуются непрерывными функциями между топологическими пространствами . Для множества всех функций из в записывают ( функтор Hom ), а для обозначения подмножества непрерывных функций используют обозначение . Здесь — биекция
в то время как uncurrying является обратным отображением. Если множество непрерывных функций из в задано компактно-открытой топологией , и если пространство локально компактно Хаусдорфово , то
является гомеоморфизмом . Это также имеет место, когда , и компактно порождены , [ 17] : глава 5 [18] хотя есть и другие случаи. [19] [20]
Одно полезное следствие заключается в том, что функция непрерывна тогда и только тогда, когда ее каррированная форма непрерывна. Другим важным результатом является то, что карта приложения , обычно называемая в этом контексте «оценкой», является непрерывной (обратите внимание, что eval — это строго иное понятие в компьютерной науке). То есть,
непрерывен, когда является компактно-открытым и локально компактным Хаусдорфовым. [21] Эти два результата являются центральными для установления непрерывности гомотопии , т.е. когда является единичным интервалом , так что можно рассматривать либо как гомотопию двух функций из в , либо, что эквивалентно, как один (непрерывный) путь в .
В алгебраической топологии каррирование служит примером двойственности Экмана–Хилтона и, как таковое, играет важную роль в различных различных ситуациях. Например, пространство циклов сопряжено с редуцированными подвесками ; это обычно записывается как
где — множество гомотопических классов отображений , а — подвеска A , а — пространство петель A . По сути, подвеску можно рассматривать как декартово произведение с единичным интервалом по модулю отношения эквивалентности, чтобы превратить интервал в петлю. Затем каррированная форма отображает пространство в пространство функций из петель в , то есть из в . [21] Тогда — сопряженный функтор , который отображает подвески в пространства петель, а декаррирование — двойственный функтор. [21]
Двойственность между конусом отображения и волокном отображения ( корасслоение и расслоение ) [17] : главы 6,7 можно понимать как форму каррирования, которая, в свою очередь, приводит к двойственности длинных точных и коточных последовательностей Пуппе .
В гомологической алгебре связь между каррированием и декаррированием известна как тензорно-гомовое присоединение . Здесь возникает интересный поворот: функтор Hom и функтор тензорного произведения могут не подниматься до точной последовательности ; это приводит к определению функтора Ext и функтора Tor .
В теории порядка , то есть теории решеток частично упорядоченных множеств , есть непрерывная функция , когда решетке задана топология Скотта . [22] Функции, непрерывные по Скотту, были впервые исследованы в попытке предоставить семантику для лямбда-исчисления (поскольку обычная теория множеств неадекватна для этого). В более общем плане функции, непрерывные по Скотту, теперь изучаются в теории доменов , которая охватывает изучение денотационной семантики компьютерных алгоритмов. Обратите внимание, что топология Скотта сильно отличается от многих общих топологий, с которыми можно столкнуться в категории топологических пространств ; топология Скотта, как правило , тоньше и не является строгой .
Понятие непрерывности появляется в теории гомотопических типов , где, грубо говоря, две компьютерные программы можно считать гомотопными, т. е. вычисляющими одни и те же результаты, если их можно «непрерывно» преобразовать из одной в другую.
В теоретической информатике каррирование предоставляет способ изучения функций с несколькими аргументами в очень простых теоретических моделях, таких как лямбда-исчисление , в котором функции принимают только один аргумент. Рассмотрим функцию, принимающую два аргумента и имеющую тип , что следует понимать так, что x должен иметь тип , y должен иметь тип , а сама функция возвращает тип . Каррированная форма f определяется как
где — абстрактор лямбда-исчисления. Поскольку карри принимает в качестве входных данных функции с типом , можно сделать вывод, что тип самого карри — это
Оператор → часто считается правоассоциативным , поэтому тип каррированной функции часто записывается как . Наоборот, применение функции считается левоассоциативным , поэтому это эквивалентно
То есть скобки не требуются для устранения неоднозначности порядка применения.
Каррированные функции можно использовать в любом языке программирования , который поддерживает замыкания ; однако некаррированные функции обычно предпочтительнее по соображениям эффективности, поскольку в этом случае можно избежать накладных расходов на частичное применение и создание замыканий для большинства вызовов функций.
В теории типов общая идея системы типов в информатике формализуется в конкретную алгебру типов. Например, при записи подразумевается, что и являются типами , в то время как стрелка является конструктором типа , в частности, типом функции или типом стрелки. Аналогично, декартово произведение типов строится конструктором типа продукта .
Теоретико-типовой подход выражен в таких языках программирования, как ML , а также в языках, созданных на его основе и вдохновленных им: Caml , Haskell и F# .
Типо-теоретический подход обеспечивает естественное дополнение к языку теории категорий , как обсуждается ниже. Это связано с тем, что категории, и в частности моноидальные категории , имеют внутренний язык , а наиболее ярким примером такого языка является просто типизированное лямбда-исчисление . Это важно в этом контексте, потому что его можно построить из одного конструктора типа, типа стрелки. Затем каррирование наделяет язык естественным типом произведения. Соответствие между объектами в категориях и типах затем позволяет языкам программирования быть переинтерпретированными как логики (через соответствие Карри–Ховарда ) и как другие типы математических систем, как будет рассмотрено далее ниже.
Согласно соответствию Карри–Ховарда , существование каррирования и декаррирования эквивалентно логической теореме (также известной как экспорт ), поскольку кортежи ( тип продукта ) соответствуют конъюнкции в логике, а тип функции соответствует импликации.
Экспоненциальный объект в категории алгебр Гейтинга обычно записывается как материальная импликация . Дистрибутивные алгебры Гейтинга являются булевыми алгебрами , а экспоненциальный объект имеет явную форму , таким образом, ясно, что экспоненциальный объект на самом деле является материальной импликацией . [23]
Вышеуказанные понятия каррирования и декаррирования находят свое наиболее общее, абстрактное выражение в теории категорий . Каррирование является универсальным свойством экспоненциального объекта и приводит к присоединению в декартовых замкнутых категориях . То есть, существует естественный изоморфизм между морфизмами из бинарного произведения и морфизмами в экспоненциальный объект .
Это обобщается до более широкого результата в замкнутых моноидальных категориях : Каррирование — это утверждение, что тензорное произведение и внутренний Hom являются сопряженными функторами ; то есть для каждого объекта существует естественный изоморфизм :
Здесь Hom обозначает (внешний) Hom-функтор всех морфизмов в категории, а обозначает внутренний hom-функтор в замкнутой моноидальной категории. Для категории множеств эти два понятия совпадают. Когда произведение является декартовым произведением, то внутренний hom становится экспоненциальным объектом .
Каррирование может сломаться одним из двух способов. Один из них — если категория не является замкнутой и, таким образом, не имеет внутреннего функтора hom (возможно, потому что существует более одного выбора для такого функтора). Другой способ — если она не является моноидальной и, таким образом, не имеет произведения (то есть, не имеет способа записи пар объектов). Категории, которые имеют и произведения, и внутренние hom, являются в точности замкнутыми моноидальными категориями.
Установка декартовых замкнутых категорий достаточна для обсуждения классической логики ; более общая установка замкнутых моноидальных категорий подходит для квантовых вычислений . [24]
Разница между этими двумя категориями заключается в том, что произведение для декартовых категорий (таких как категория множеств , полных частичных порядков или алгебр Гейтинга ) — это просто декартово произведение ; оно интерпретируется как упорядоченная пара элементов (или список). Просто типизированное лямбда-исчисление — это внутренний язык декартовых замкнутых категорий; и именно по этой причине пары и списки являются основными типами в теории типов LISP , Scheme и многих функциональных языков программирования .
Напротив, произведение для моноидальных категорий (таких как гильбертово пространство и векторные пространства функционального анализа ) является тензорным произведением . Внутренний язык таких категорий — линейная логика , форма квантовой логики ; соответствующая система типов — линейная система типов . Такие категории подходят для описания запутанных квантовых состояний и, в более общем плане, допускают обширное обобщение соответствия Карри–Ховарда для квантовой механики , кобордизмов в алгебраической топологии и теории струн . [3] Линейная система типов и линейная логика полезны для описания примитивов синхронизации , таких как замки взаимного исключения и работа торговых автоматов.
Каррирование и частичное применение функции часто смешивают. [1] [2] Одно из существенных различий между ними заключается в том, что вызов частично примененной функции возвращает результат немедленно, а не другую функцию ниже по цепочке каррирования; это различие можно наглядно проиллюстрировать для функций, арность которых больше двух. [25]
При наличии функции типа каррирование производит . То есть, в то время как оценка первой функции может быть представлена как , оценка каррированной функции будет представлена как , применяя каждый аргумент по очереди к функции с одним аргументом, возвращенной предыдущим вызовом. Обратите внимание, что после вызова у нас остается функция, которая принимает один аргумент и возвращает другую функцию, а не функция, которая принимает два аргумента.
Напротив, частичное применение функции относится к процессу фиксации ряда аргументов функции, производя другую функцию меньшей арности. Учитывая определение выше, мы могли бы зафиксировать (или «связать») первый аргумент, производя функцию типа . Оценка этой функции может быть представлена как . Обратите внимание, что результатом частичного применения функции в этом случае является функция, которая принимает два аргумента.
Интуитивно, частичное применение функции говорит: «если вы фиксируете первый аргумент функции, вы получаете функцию оставшихся аргументов». Например, если функция div обозначает операцию деления x / y , то div с параметром x , фиксированным на 1 (т.е. div 1), является другой функцией: такой же, как функция inv , которая возвращает мультипликативную инверсию своего аргумента, определяемую как inv ( y ) = 1/ y .
Практическая мотивация частичного применения заключается в том, что очень часто функции, полученные путем предоставления некоторых, но не всех аргументов функции, полезны; например, во многих языках есть функция или оператор, похожий на plus_one
. Частичное применение упрощает определение этих функций, например, путем создания функции, которая представляет оператор сложения с 1-й границей в качестве своего первого аргумента.
Частичное применение можно рассматривать как оценку каррированной функции в фиксированной точке, например, при заданном и затем или просто где каррирует первый параметр f.
Таким образом, частичное применение сводится к каррированной функции в фиксированной точке. Кроме того, каррированная функция в фиксированной точке является (тривиально) частичным применением. Для дальнейшего доказательства отметим, что для любой функции , функция может быть определена так, что . Таким образом, любое частичное применение может быть сведено к одной операции каррирования. Таким образом, каррирование более уместно определить как операцию, которая во многих теоретических случаях часто применяется рекурсивно, но которая теоретически неотличима (если рассматривать ее как операцию) от частичного применения.
Таким образом, частичное применение можно определить как объективный результат однократного применения оператора карри к некоторому порядку входных данных некоторой функции.
Существует устройство, созданное Шёнфинкелем, для сведения операторов с несколькими операндами к последовательному применению операторов с одним операндом.
В последней строке мы использовали трюк, называемый Каррированием (в честь логика Х. Карри), чтобы решить проблему введения бинарной операции в язык, где все функции должны принимать один аргумент. (Рефери замечает, что, хотя «Каррирование» звучит вкуснее, «Шенфинкелинг» может быть точнее.)Переиздано как Reynolds, John C. (1998). "Definitional Interpreters for Higher-Order Programming Languages". Вычисления высшего порядка и символьные вычисления . 11 (4). Бостон: Kluwer Academic Publishers: 363– 397. doi :10.1023/A:1010027404223. 13 – через Сиракузский университет: Колледж инженерии и компьютерных наук - Бывшие кафедры, центры, институты и проекты.
Некоторые современные логики называют этот способ рассмотрения функции «каррированием», потому что я широко его использовал; но у Шёнфинкеля эта идея возникла примерно за 6 лет до меня.
{{cite book}}
: CS1 maint: date and year (link)