Я новичок в Clojure и нахожу свои морские ноги. Мне было интересно, считается ли это хорошей или плохой практикой, с точки зрения функционального программирования, размещать функции в картах Clojure, а затем передавать эти карты вокруг как квазиобъекты, как это часто делается в JavaScript. Пояснения также будут оценены.
Это плохая идея поместить функции в карты Clojure, например, в JavaScript?
Ответ 1
Clojure мультиметоды - это, по существу, карты функций, поэтому нет, это совсем не плохая идея.
Ответ 2
Сделайте это, если это сделает ваш код короче или проще для понимания или тестирования или отладки.
Или, если бы вам просто хотелось. Доверяйте своему мнению.
Ответ 3
Было бы плохо по нескольким причинам:
- Это не нужно. В JavaScript мы делаем это, чтобы использовать простые карты как объекты, подобные Java. В Clojure, если вы действительно этого хотите, вы можете использовать фактический объект Java через Java interop.
- Если вы размещаете функции на картах или используете реальный объект Java, вы выполняете объектно-ориентированное программирование в языковой среде, которая была разработана для функционального программирования. Первая проблема, которая приходит с этим, заключается в том, что ваш код просто не вписывался. Это выглядело бы странно, неуклюже и, возможно, даже слишком сложно в Clojure. На данный момент я не говорю, что ООП обязательно плохо, просто чтобы лучше писать ООП на языках, которые были предназначены для него.
- Наиболее важным моментом в отношении такого стиля кодирования является то, что программа, подобная ООП, неизбежно опиралась бы на объекты, которые имеют некоторое состояние, и их функции-члены (похожие на методы), которые изменяют это состояние. Если вы используете состояние, то ваши функции не являются чистыми, и вы не можете использовать все функциональные качества, например, простое распараллеливание.
Короче говоря, если вы используете Clojure для выполнения ООП, вы будете только раздражаться. Вы можете сделать ООП более легко в тоннах других языков, например, скажем Groovy. Если вы хотите использовать Clojure, сделайте это функционально.
До сих пор я писал не делать этого и почему бы не сделать это. Теперь вы можете спросить меня: Итак, как мне писать функции в Clojure?
Напишите функции, которые принимают вашу структуру данных (например, карту, список, объект... независимо) в качестве параметра. Итак, вместо:
foo.bar();
Вы бы определили его так:
(defn bar [foo]
;stuff
)
И назовите его следующим образом:
(bar foo)
Теперь для чистой функции bar
объект foo
остается неизменным после оценки функции, и у вас нет общего состояния объекта, о котором нужно беспокоиться, если вы решили распараллелить свой код, который вы бы если вы делаете что-то ООП.
Кроме того, это может выглядеть как небольшая разница, но обратите внимание, что определение функции bar
- это объект, полностью независимый от структуры данных foo
. Кроме того, foo
содержит только данные, а не поведение - все поведение в функции. Это разделение дает вам гораздо большую свободу при кодировании.
Ответ 4
Это работает - и в некотором смысле это естественно, если вы рассматриваете функции как первоклассные объекты на языке (как и все функциональные программисты должны!)
Однако - это требует реальной заботы, потому что в основном вы выполняете код перемежения с данными. Это скорее похоже на смешивание вашей модели данных с презентационным кодом в MVC. Да, могут быть ситуации, когда это имеет смысл, но общим принципом было бы избежать этого.
Стиль, с которым я медленно сблизился примерно через год Clojure, выглядит следующим образом:
- Несколько refs/atom/vars верхнего уровня для прозрачного управления изменяемым состоянием (престиж для Rich Hickey!)
- Большие, вложенные неизменные структуры данных в ссылках верхнего уровня
- Чистые функции для обработки практически всей обработки.
- Несколько функций, оканчивающиеся на! для тех неприятных побочных эффектов, используемых экономно
- Много и много тестов! Это действительно важно, потому что с динамической типизацией и очень гибкими структурами данных вам нужно проверить почти все ваши предположения.
- Java (к сожалению!) для оптимизации чувствительных к производительности разделов кода, как правило, путем определения легкого, неизменяемого Java-класса, который хорошо работает в структурах данных Clojure
Это решает большинство вещей под солнцем, одна вещь, которую я все еще пытаюсь выяснить, - лучший способ создать объекты с очень полиморфным поведением. Основные параметры, которые я вижу:
- Использовать протоколы с deftype/defrecord - высокая производительность, идиоматическая Clojure, но вы получаете только одну отправку по типу, которая недостаточна для некоторых сильно полиморфных ситуаций.
- Создавать функции, которые ведут себя полиморфным способом с помощью множества функций макроса/более высокого порядка - работает, но может очень запутанно/сложно поддерживать
- Положите функции внутри карт! Да, он чувствует себя не так, но он работает!
По-прежнему не удалось выяснить, куда идти..... но у меня есть догадка, что функции могут проскользнуть в карты немного больше в будущем....
Ответ 5
Если вам действительно нужно сделать объектно-ориентированное программирование в Clojure, существует несколько способов сделать это.
Первый способ - использовать макросы deftype
, defrecord
и defprotocol
.
Второй способ - использовать мультиметоды в сочетании с картой или типом записи для хранения данных.
Третий способ - использовать универсальный шаблон дизайна, описанный Steve Yegge, или более конкретное введение Clojure можно найти в Книга Криса Хаузера и Майкла Фогуса Радость Clojure.
Третий подход очень похож на то, что вы ожидаете увидеть в JavaScript, и первый подход, не считающийся ООП в традиционном смысле, является наиболее распространенным в "современном" идиоматическом Clojure.
Ответ 6
Пространства имен - это буквально карты функций. Гораздо проще использовать их для организации ваших функций. Однако, если вы столкнетесь с ограничением пространств имен для вашего варианта использования, вы можете рассмотреть возможность размещения функций на карте.
Как обычно, когда вы идете по проторенной дорожке, просто не задумывайтесь о том, почему вы не идете по очевидному маршруту.