Отказ от ответственности: это собирается сойти с ума. Я не настолько разочарован «проблемами», которые я несу нам, как кажется; скорее это используется, чтобы подчеркнуть, почему мои мыслительные процессы сделали то, что сделали. Я даже не собираюсь использовать заголовки заголовков, что странно для меня 🙂
У меня недавно был мыслительный процесс по поводу вызова функций. Мне постоянно было неприятно, как функциональные языки (и даже другие языки иногда) достигают определенной цели.
Эта цель — цепочка вызовов.
По-моему, никто не понимает это правильно. Единственный раз, когда он работает хорошо, это беглые интерфейсы объектов. Давайте рассмотрим простой пример 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 Джейкоба Циммермана в блоге « Идеи программирования с Джейком» . |