Статьи

Новая идея для вызова функций

Отказ от ответственности: это собирается сойти с ума. Я не настолько разочарован «проблемами», которые я несу нам, как кажется; скорее это используется, чтобы подчеркнуть, почему мои мыслительные процессы сделали то, что сделали. Я даже не собираюсь использовать заголовки заголовков, что странно для меня 🙂

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

Эта цель — цепочка вызовов.

По-моему, никто не понимает это правильно. Единственный раз, когда он работает хорошо, это беглые интерфейсы объектов. Давайте рассмотрим простой пример filter списка (называемого myList ) по четным числам (функция предиката будет называться evens ), а затем map ping с squared результата:

В типичном мультипарадигмальном языке с универсальными функциями для filter и map , такими как Python:

1
map(squared, filter(evens, myList))

Или язык шуток:

1
(map squared (filter evens myList))

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

Примечание: Также обратите внимание, что эти функции помещают переданные функции первыми в списке аргументов, а не в списке. Я лично всегда думал, что лучше всего поставить первичный объект, над которым работаем, первым. Единственная причина, по которой я могу подумать о включении функций в первую очередь, заключается в том, что они обеспечивают более легкое создание функций с более высокой абстракцией (таких как создание функции sum из метода Reduction) посредством каррирования или использования партиала Python. Порядок аргументов снова появится в «решении».

Одна из полезных вещей, которую предоставляют многие из этих языков, — это функция композиции, которая позволяет объединять вызовы функций в один. Если мы карри или partial измерим функции map и filter в map_squared и filter_evens , соответственно, мы можем сделать вызов compose следующим образом:

1
compose(map_squared, filter_evens, myList)

Или вот так в шутку:

1
(compose map_squared filter_evens myList)

Заметьте что-нибудь? Мы все еще читаем справа налево! По какой-то причине было принято решение, что compose(f, g, data) должен читаться как « f of g » (как математик) вместо « f затем g » (как любой другой человек). Конечно, функциональное программирование (и, вероятно, 90% других программ) было разработано теми, кто интересуется математикой. Черт возьми, я ЛЮБЛЮ математику, взяв «Исчисление I» дважды (первый раз в старшей школе, второй в колледже, оба раза для развлечения) Но это все еще не тот способ чтения и записи вызовов функций, который мне нравится.

Что мне нравится? Как упоминалось ранее, свободно работающие OO API работают прекрасно для этого. В идеале я хотел бы позвонить

1
myList.filter(evens).map(squared)

Некоторые могут признать, что это похоже на то, как выглядит Stream API Java 8. Это очень близко к тому, как выглядит Котлин.

Вот проблемы с OO, хотя:

  • ОО доступен только для ОО языков (очевидно)
  • Чтобы реализовать эти плавные API, вы должны либо изначально встроить их в объекты, либо создать какой-то класс-оболочку, который их реализует.
  • Даже если у вас есть свободный API, позже может потребоваться какая-то новая функция, которая по какой-то причине не может быть добавлена.

Последние две проблемы решаются с помощью методов расширения (которые включаются только в языки OO по понятным причинам), но я решил прийти с точки зрения языка, который является либо чисто функциональным, либо, по крайней мере, частично OO без способ создания методов расширения.

Мое единственное решение — создать новый, необязательный способ вызова функций. Это было частично вдохновлено методами Python, которые уже делают это, но делают это наоборот; они позволяют вызывать методы объекта как обычные функции, тогда как мое решение позволяет вызывать нормальные функции как методы объекта. Для объяснения того, как Python делает первое, смотрите эту статью о том, почему я считаю, что аргумент self был гениальным ходом .

Итак, если вы еще не поняли, мое решение — позволить первому аргументу функции действовать так, как будто это объект, для которого вы вызываете функцию (как «метод»). Таким образом, вы можете взять много встроенных функций Python, таких как len , iter , next и т. Д., И вызывать их так:

1
2
3
4
5
myList.len()
 
myList.iter()
 
myIterator.next()

Это больше соответствует другим языкам OO.

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

1
squared.map(evens.filter(myList))

Который даже не начинает решать проблему.

Как насчет карри или использования partial ? Что ж, похоже, вам придется вручную определять свою функцию sum , а не использовать карри. Например (при условии, что сокращение сейчас занимает список первым):

1
2
3
def sum(sequence):
 
return reduce(sequence, operator.add)

Что касается частичного, это все еще работает, если вы используете ключевое слово аргумент:

1
sum = functools.partial(function=operator.add)

Как насчет губ? У них нет синтаксиса вызова OO. Как это может работать с лиспами? Просто: новый способ может поставить первый аргумент первым, а затем имя вызываемой функции, с префиксом точки. Например, новый синтаксис карты и фильтра будет выглядеть так:

1
((myList .filter evens) .map squared)

Так что ты думаешь? Вам нравится идея? Я хотел бы создать язык с такой функциональностью, но я слишком занят другими идеями, чтобы позволить себе заниматься созданием нового языка. Я бы предпочел, чтобы языки, которые сейчас существуют, добавили этот синтаксический сахар в свой репертуар. Скрещенные пальцы. Потому что мне надоело писать классы-обертки для такого рода вещей.

Ссылка: Новая идея для вызова функций от нашего партнера JCG Джейкоба Циммермана в блоге « Идеи программирования с Джейком» .