Является ли core.async вопреки принципам Clojure?
Я видел много программистов Clojure, энтузиастов по поводу новой библиотеки core.async и, хотя мне кажется очень интересным, мне нелегко видеть, как она соответствует принципам Clojure, поэтому у меня есть следующие вопросы:
- Он использует изменяемое состояние везде, так как названия функций предполагают наличие восклицательного знака, например alt!, put!, > !, и другие. Если вы поместите или возьмите значение из канала, этот канал будет изменен на место. Разве это не противоречит философии Clojure, предпочитающей неизменные структуры данных, чистые функции и т.д.? Или core.async используется для использования только там, где вообще невозможно избежать изменчивых вещей?
-
Так как "go" представляет собой макрос (таким образом, изменяя структуру кода) и обеспечивает "<!" используется непосредственно в блоке go, невозможно использовать "<!" внутри другой функции, например:
(defn take-and-print [c]
(println (<! c)))
(def ch (chan 1))
(>!! ch 123)
(go (take-and-print ch))
Assert failed: <! used not in (go ...) block
Мне кажется, что это предотвращает простоту и удобство. Почему это не проблема?
-
Возможно, из-за предыдущих двух проблем много кода с core.async использует конструкции нижнего уровня, такие как loop/recur вместо map/filter/reduce. Разве это не шаг назад?
Где я пропущу точку?
Спасибо заранее.
Ответы
Ответ 1
Первая проблема - да, основные операции - побочные эффекты. Однако в каналах нет проблем, обычно связанных с изменяемыми ссылками, поскольку они не представляют собой "место" - каналы непрозрачны, вы не можете их проверять, на самом деле вы даже не можете запросить, закрыт ли канал или нет за пределами чтения ноль.
Вторая проблема - делать что-то большее, чем мелкий доход, означает преобразование всей программы. Это компромисс, и я считаю разумным. Уровень композиции - это каналы, которые не идут на блоки, и они составляют просто отлично.
В конечном итоге вы можете легко выполнить Rx-стиль карты/фильтр/уменьшить операции по каналам, и люди уже сделали это.
Ответ 2
Ограничение макроса go (его локальность) также является особенностью: оно обеспечивает локальность исходного кода операций с состоянием.
Ответ 3
-
это несколько наоборот, Core.async может использоваться только в системах, где неизменяемость является нормой. Таким образом, принципы Clojure позволяют использовать core.async, а не обратный.
-
Это ограничение, также в другом месте в Clojure, ограничение анонимных функций, не входящих в символ %
, похоже, по крайней мере, дает ту же идею. Не то, чтобы найти другой случай ограничения, делает его лучше, конечно.
-
Я не видел этого себя, хотя это было бы шагом назад, если бы вы пытались сделать простой код и очистились, когда выражали в одном виде, а затем выражали его таким образом, который есть. не так...
Ответ 4
Богатый Хикки сказал в одном из лекций blip.tv, что Clojure является "85% функциональным". Мне нравится видеть core.async как часть остальных 15%. Core.async отлично подходит для надежного взаимодействия с пользователем между прочим, что было бы сделано с помощью promises, задержек и других вещей, скорее всего, более запутанным способом.
Ответ 5
Каждая программа состоит из двух частей: одна часть, которая всегда содержит данные не в режиме разговора, выплевывает и т.д. Для этой части мы знаем, что имеет core.async, но ядро .async имеет изменяемые вещи, но обратите внимание на две вещи. Состояние каналов в основном управляется библиотекой. То, что вы наделите, это то, что можно назвать flowstate. Это означает, что, когда вы помещаете что-то вроде канала, вы не ожидаете вернуться к нему или даже изменить его.
Core.async приятно управлять этой частью вашей программы. В остальном все преобразования и вычисления ваших данных clojure стараются дать вам хорошие инструменты, чтобы сделать это функционально.
Мне кажется, что это предотвращает простоту и удобство. Почему это не проблема?
Есть два мира: синхронизация и асинхронный мир. Вы можете положить вещи или взять вещи из асинхронного wrold с помощью! и возьми! но другие, то (и, возможно, некоторые другие функции), эти миры разделены друг от друга. clojure не хочет становиться полностью асинхронным. Функции и преобразование данных - это то, что нужно скомпоновать.
Возможно, как следствие предыдущих двух проблем, много кода с core.async использует конструкции нижнего уровня, такие как loop/recur вместо карта/фильтр/уменьшить. Разве это не шаг назад
Операция, подобная этим каналам, будет возможна. Core.async еще молод, и не все возможные конструкции и функции были написаны еще.
Но в общем случае, если у вас большие преобразования данных, вы действительно не хотите делать их в асинхронном мире, вы хотите, чтобы они были в коллекции, а затем бросали что-то вроде структуры сокращения на нем.
Главное, чтобы понять это, core.async - это не новый стандарт, это еще одна вещь, которая помогает вам в программировании. Иногда вам нужны STM, иногда агенты, иногда CSP, и clojure хочет предоставить вам все варианты.
Одна из причин, по которой люди любят core.async, - это то, что это помогает с некоторыми вещами, которые в действительности действительно трудно справиться, например, с обратными вызовами.
Ответ 6
возможно возможное решение использовать (<! c)
макрос вне go можно сделать с помощью макроса и его времени расширения макроса:
Это мой пример:
(ns fourclojure.asynco
(require [clojure.core.async :as async :refer :all]))
(defmacro runtime--fn [the-fn the-value]
`(~the-fn ~the-value)
)
(defmacro call-fn [ the-fn]
`(runtime--fn ~the-fn (<! my-chan))
)
(def my-chan (chan))
(defn read-channel [the-fn]
(go
(loop []
(call-fn the-fn)
(recur)
)
))
(defn paint []
(put! my-chan "paint!")
)
И проверить это:
(read-channel print)
(repeatedly 50 paint)
Я пробовал это решение в вложенном режиме и также работал. Но я не уверен, что это может быть правильный путь.