Подтвердить что ты не робот

Является ли 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. Разве это не шаг назад?

Где я пропущу точку?

Спасибо заранее.

4b9b3361

Ответ 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)

Я пробовал это решение в вложенном режиме и также работал. Но я не уверен, что это может быть правильный путь.