Я думаю о том, чтобы сделать функции каррирования и вариации доступными в динамически типизированном функциональном языке программирования, но мне интересно, возможно ли это или нет.
Вот несколько псевдокодов:
sum = if @args.empty then 0 else @args.head + sum @args.tail
который якобы суммирует все свои аргументы. Затем, если sum
сам трактуется число, то результат 0
. например,
sum + 1
равен 1, при условии, что +
может работать только на числа. Однако, даже если sum == 0
это правда, sum
все равно сохранит свое значение и функциональное свойство независимо от того, сколько аргументов задано (например, «частично применено» и «вариадно» одновременно), например, если я объявлю
g = sum 1 2 3
затем g
равен 6
, однако, мы можем еще применить g
. Например, g 4 5 == 15
это правда. В этом случае мы не можем заменить объект g
литералом 6
, потому что, хотя они и дают одно и то же значение, когда рассматриваются как целое число, они содержат разные коды внутри.
Если этот дизайн используется на реальном языке программирования, это вызовет какую-то путаницу или двусмысленность?
источник
sum
это0
без аргумента и рекурсивно вызывает себя с аргументом.reduce
?args
:empty
,head
, иtail
. Это все функции списков, предполагающие, что, возможно, проще и понятнее было бы использовать список, в котором будут использоваться различные варианты. (Итак,sum [1, 2, 3]
вместоsum 1 2 3
)Ответы:
Как можно реализовать varargs? Нам нужен какой-то механизм для оповещения об окончании списка аргументов. Это может быть
Оба эти механизма могут использоваться в контексте каррирования для реализации varargs, но правильная типизация становится серьезной проблемой. Давайте предположим, что мы имеем дело с функцией
sum: ...int -> int
, за исключением того, что эта функция использует каррирование (поэтому у нас на самом деле есть тип более похожийsum: int -> ... -> int -> int
, за исключением того, что мы не знаем количество аргументов).Случай: значение терминатора: Позвольте
end
быть специальным терминатором, иT
будет типsum
. Теперь мы знаем , что применительно кend
функции возвращает:sum: end -> int
и, применяемые к междунар мы получаем другую сумму-как функция:sum: int -> T
. ПоэтомуT
является объединение этих типов:T = (end -> int) | (int -> T)
. ПодстановкойT
, мы получаем различные возможные типы , такие какend -> int
,int -> end -> int
,int -> int -> end -> int
и т.д. Тем не менее, большинство систем типа не учитывают таких типов.Случай: явная длина: первый аргумент функции vararg - это число varargs. Итак
sum 0 : int
,sum 1 : int -> int
иsum 3 : int -> int -> int -> int
т. Д. Это поддерживается в некоторых системах типов и является примером зависимой типизации . На самом деле, число аргументов будет параметром типа, а не обычным параметром - не имеет смысла зависеть от арности функции, зависящей от значения времени выполнения,s = ((sum (floor (rand 3))) 1) 2
очевидно, что она не типизирована: это дает либоs = ((sum 0) 1) 2 = (0 1) 2
, либоs = ((sum 1) 1) 2 = 1 2
, либоs = ((sum 2) 1) 2 = 3
.На практике ни один из этих методов не следует использовать, поскольку они подвержены ошибкам и не имеют (значимого) типа в системах общих типов. Вместо этого, просто передать список значений в качестве одного параметра Я :
sum: [int] -> int
.Да, объект может отображаться как функция и значение, например, в системе типов с принуждениями. Позвольте
sum
бытьSumObj
, который имеет два принуждения:coerce: SumObj -> int -> SumObj
позволяетsum
использовать в качестве функции, иcoerce: SumObj -> int
позволяет нам извлечь результат.Технически, это вариант описанного выше значения терминатора
T = SumObj
,coerce
который является и не является оберткой для данного типа. Во многих объектно-ориентированных языках это легко реализовать с перегрузкой операторов, например, C ++:источник
..., force=False)
для принудительного применения начальной функции.curryList : ([a] -> b) -> [a] -> [a] -> b, curryList f xs ys = f (xs ++ ys)
.Возможно, вы захотите взглянуть на эту реализацию printf в Haskell вместе с описанием того, как она работает . На последней странице есть ссылка на статью Олега Киселева о таких действиях, которую также стоит прочитать. На самом деле, если вы разрабатываете функциональный язык, сайт Олега, вероятно, должен быть обязательным для чтения.
На мой взгляд, эти подходы немного взломать, но они показывают, что это возможно. Однако, если ваш язык имеет полностью зависимую типизацию, это намного проще. Функция с переменным числом для суммирования целочисленных аргументов может выглядеть примерно так:
Абстракция для определения рекурсивного типа без необходимости давать ему явное имя может упростить написание таких функций.
Редактировать: конечно, я просто прочитал вопрос еще раз, и вы сказали, что язык с динамической типизацией, в этот момент очевидно, что механика типов на самом деле не актуальна, и поэтому ответ @ amon, вероятно, содержит все, что вам нужно. О, хорошо, я оставлю это здесь на случай, если кто-нибудь столкнется с этим, задаваясь вопросом, как это сделать на статическом языке ...
источник
Вот версия для каррирования функций с переменными числами в Python3, которая использует подход «терминатора» @amon, используя дополнительные аргументы Python:
Возвращенная функция
f
собирает аргументы, переданные ей в последовательных вызовах, в массиве, который связан с внешней областью. Только когдаforce
аргумент равен true, исходная функция вызывается со всеми собранными до сих пор аргументами.Предостережения этой реализации заключаются в том, что вам всегда нужно передавать первый аргумент,
f
чтобы вы не могли создать «thunk», функцию, где все аргументы связаны и могут вызываться только с пустым списком аргументов (но я думаю, что это соответствует типичная реализация карри).Еще одно предостережение: если вы передадите неправильный аргумент (например, неправильного типа), вам придется повторно каррировать исходную функцию. Нет другого способа сбросить внутренний массив, это делается только после успешного выполнения карри функции.
Я не знаю, может ли ваш упрощенный вопрос «может ли объект быть одновременно функцией и не функциональным значением?», Может быть реализован в Python, так как ссылка на функцию без скобок оценивает внутренний объект функции , Я не знаю, можно ли это согнуть, чтобы вернуть произвольное значение.
Это, вероятно, будет легко в Lisp, так как символы Lisp могут иметь значение и значение функции одновременно; значение функции просто выбирается, когда символ появляется в положении функции (как первый элемент в списке).
источник