Разве core.async не противоречит принципам Clojure?

Я видел, как многие программисты Clojure с энтузиазмом относятся к новой библиотеке core.async, и, хотя она кажется очень интересной, мне трудно понять, насколько она соответствует принципам Clojure, поэтому у меня есть следующие вопросы:

  1. Он повсюду использует изменяемое состояние, о чем свидетельствуют имена функций, содержащие восклицательный знак, например alt !, put !,> !, и другие. Если вы вводите или берете значение из канала, этот канал изменяется на месте. Разве это не противоречит философии Clojure о предпочтении неизменяемых структур данных, чистых функций и так далее? Или core.async предназначен для использования только там, где невозможно избежать изменчивых вещей?
  2. Поскольку "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
    

    Мне кажется, что это мешает простоте и компоновке. Почему это не проблема?

  3. Возможно, как следствие двух предыдущих проблем, большая часть кода с core.async использует конструкции более низкого уровня, такие как loop / recur вместо map / filter / reduce. Разве это не шаг назад?

Где я упускаю главное?

Заранее спасибо.


person aeuhuea    schedule 13.08.2013    source источник
comment
Ваше первое замечание упускает из виду различие Clojure между ссылками и значениями. Вы не изменяете значения в core.async.   -  person deprecated    schedule 13.08.2013
comment
А как насчет свопа! и сбросить! (Например)? Оба находятся в коде Clojure и изменяют состояние.   -  person Chiron    schedule 13.08.2013
comment
Я думаю, что его (ее) точка зрения заключается в том, что, хотя в Clojure существуют изменяемые функции, они не должны использоваться обычно и служат спасательным люком на случай, если они вам действительно понадобятся.   -  person SCdF    schedule 14.08.2013
comment
@Chiron, Clojure имеет способы изменения состояния, если это необходимо, но они не должны использоваться все время. Мой первый вопрос в основном спрашивал, так ли это с core.async.   -  person aeuhuea    schedule 14.08.2013
comment
@vemv Верно, вы не изменяете значения, которые вводите в каналы. Но вы изменяете состояние самих каналов.   -  person aeuhuea    schedule 14.08.2013
comment
Что согласуется с атомами, ссылками, агентами и т. Д. Выражение изменений с течением времени - основная идея Clojure.   -  person deprecated    schedule 14.08.2013
comment
Что касается моего третьего вопроса, похоже, Рич Хики только что создал много новых высокоуровневых функций в core.async, включая map, filter и reduce для каналов. Посмотрите, что он сделал вчера: github.com/clojure/core/ync/ а>   -  person aeuhuea    schedule 24.09.2013


Ответы (6)


Первая проблема - да, основные операции - это побочные эффекты. Однако у каналов нет проблем, обычно связанных с изменяемыми ссылками, поскольку они не представляют «место» - каналы непрозрачны, вы не можете их проверить, фактически вы даже не можете запросить, закрыт ли канал или нет, кроме чтения. ноль.

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

И последнее: вы можете легко выполнять операции map / filter / reduce в стиле Rx по каналам, и люди уже сделали это.

person dnolen    schedule 13.08.2013
comment
«они не представляют место» - А как насчет admix и unmix? - person erikprice; 03.12.2014

Ограничение макроса go (его локальность) также является особенностью: он обеспечивает локальность исходного кода для операций с отслеживанием состояния.

person cgrand    schedule 14.08.2013
comment
Интересный момент. Это действительно упрощает обнаружение мутаций каналов, поскольку нам не нужно смотреть определения других функций, вызываемых в блоке перехода. С этой точки зрения это преимущество, в то время как некоторые другие ответы показывают это как приемлемый недостаток. - person aeuhuea; 14.08.2013
comment
Привет, cgrand, я опубликовал возможное решение, чтобы избежать ограничения с помощью макроса go, как вы думаете, это может быть практическое решение? - person tangrammer; 23.10.2013

  1. это несколько наоборот, Core.async можно использовать только в системах, где неизменяемость является нормой. Итак, принципы Clojure позволяют использовать core.async, а не наоборот.

  2. Это ограничение, встречается и в другом месте в clojure, ограничение анонимных функций, не составляющих символ %, похоже, по крайней мере, представляет ту же идею. Не то чтобы обнаружение другого случая ограничения, конечно, улучшило бы ситуацию.

  3. Я не видел этого сам, хотя это было бы шагом назад, если бы вы пытались взять код, который был простым и чистым, когда он выражался одним способом, а затем выражал его таким образом ... не таким .. .

person Arthur Ulfeldt    schedule 13.08.2013

Рич Хики сказал в одной из лекций на blip.tv, что Clojure "на 85% функциональна". Мне нравится видеть core.async как часть остальных 15%. Core.async отлично подходит для прочного взаимодействия с пользователем, среди прочего, что могло бы быть сделано с помощью обещаний, задержек и других вещей, вероятно, более беспорядочным способом.

person claj    schedule 13.08.2013

Каждая программа состоит из двух частей: одна часть, которая всегда не содержит данных о функциях, выводит их и так далее. В этой части мы знаем, что есть core.async, конечно, в core.async есть изменяемые вещи, но обратите внимание на две вещи. Состояние каналов в основном управляется библиотекой. То, что вы на него надеваете, можно назвать flowstate. Это означает, что когда вы помещаете что-то вроде канала, вы не ожидаете, что вернетесь к нему или даже измените его.

Core.async отлично подходит для управления этой частью вашей программы. В остальном, включая все преобразования и вычисления ваших данных, clojure изо всех сил старается предоставить вам хорошие инструменты для функциональной реализации.

Мне кажется, что это мешает простоте и компоновке. Почему это не проблема?

Есть два мира: синхронный и асинхронный. Вы можете помещать данные или извлекать их из асинхронной среды с помощью команды put! и возьми! но кроме этого (и, возможно, какой-то другой функции) эти миры отделены друг от друга. Clojure не хочет становиться полностью асинхронным языком. Функции и преобразование данных должны быть составными.

Возможно, как следствие двух предыдущих проблем, большая часть кода с core.async использует конструкции более низкого уровня, такие как loop / recur вместо map / filter / reduce. Разве это не шаг назад

Подобная работа по каналам будет возможна. Core.async еще молод, и еще не все возможные конструкции и функции написаны.

Но в целом, если у вас есть большие преобразования данных, вы действительно не хотите делать их в асинхронном мире, вы хотите иметь их в коллекции, а затем использовать что-то вроде структуры reducres.

Главное понять, core.async - это не новый стандарт, это еще одна вещь, которая помогает вам программировать. Иногда вам нужен STM, иногда агенты, иногда - это зависит от CSP, и clojure хочет предоставить вам все возможности.

Одна из причин, по которой людям нравится core.async, заключается в том, что он помогает с некоторыми вещами, с которыми в противном случае действительно сложно справиться, например, с обработкой обратных вызовов.

person nickik    schedule 20.01.2014

возможно, возможное решение использовать (<! 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)

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

person tangrammer    schedule 23.10.2013