简体   繁体   English

是不是与Clojure原则相反的core.async?

[英]Isn't core.async contrary to Clojure principles?

I have seen many Clojure programmers enthusiastic about the new core.async library and, though it seems very interesting, I am having a hard time seeing how it conforms to Clojure principles, so I have these questions: 我见过很多Clojure程序员热衷于新的core.async库,虽然看起来非常有趣,但我很难看到它是如何符合Clojure原则的,所以我有这些问题:

  1. It uses mutable state everywhere, as the function names suggest by having an exclamation mark, like alt!, put!, >!, and others. 它在任何地方都使用可变状态,因为函数名称通过带有感叹号来表示,例如alt!,put!,>!和其他。 If you put or take a value from a channel, that channel is modified inplace. 如果您从频道中输入或获取值,则会在该位置修改该频道。 Isn't it contrary to Clojure philosophy of preferring immutable data-structures, pure functions and so on? 是否与Clojure哲学相反,它更倾向于使用不可变数据结构,纯函数等等? Or is core.async made to be used only where mutable things could not be avoided at all? 或者是core.async仅在无法避免可变事物的情况下使用?
  2. Since "go" is a macro (thus modifying code structure) and ensures "<!" 因为“go”是一个宏(从而修改代码结构)并确保“<!” is used directly in a go-block, it is not possible to use "<!" 直接在go-block中使用,不能使用“<!” inside another function, like this: 在另一个函数里面,像这样:

     (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 

    It seems to me that this prevents simplicity and composability. 在我看来,这可以防止简单性和可组合性。 Why is it not a problem? 为什么不是问题?

  3. Maybe as a consequence of the previous two issues, a lot of code with core.async uses lower-level constructs such as loop/recur instead of map/filter/reduce. 也许作为前两个问题的结果,core.async的许多代码使用较低级别的构造,例如loop / recur而不是map / filter / reduce。 Isn't it a step backwards? 这不是倒退吗?

Where am I missing the point? 我在哪里错过了这一点?

Thanks in advance. 提前致谢。

The first concern - yes the core operations are side effects. 第一个问题 - 是核心操作是副作用。 However channels don't have the problems normally associated with mutable references as they don't represent a "place" - channels are opaque, you cannot inspect them, in fact you can't even query whether a channel is closed or not beyond reading nil. 然而,通道没有通常与可变引用相关的问题,因为它们不代表“地方” - 通道是不透明的,你无法检查它们,实际上你甚至无法查询通道是否关闭或超出阅读零。

The second concern - doing anything more than shallow yield would mean whole program transformation. 第二个问题 - 做任何不仅仅是浅层产量将意味着整个项目的转型。 This is a tradeoff and I think a reasonable one. 这是一个权衡,我认为是合理的。 The level of composition is channels not go blocks and they compose just fine. 组成的水平是通道而不是块,它们组成很好。

The final concern, you can easily do Rx style map/filter/reduce operations over channels and people have already done so. 最后的关注点,你可以很容易地做Rx风格的地图/过滤/减少操作的渠道,人们已经这样做了。

go宏(它的位置)的限制也是一个特性:它强制执行有状态操作的源代码局部性。

  1. it's somewhat the other way around, Core.async can only be used in systems where Immutability is the norm . 反过来说, Core.async只能用于不可变性是常态的系统中 So Clojure's principles enable core.async rather than the inverse. 所以Clojure的原则启用了core.async而不是反向。

  2. This is a limitation, happens in other place in clojure as well, the limitation of anonymous functions not composing with the % symbol seems to at least present the same idea. 这是一个限制,也发生在clojure的其他地方,匿名函数的限制没有与%符号组成似乎至少表现出相同的想法。 Not that finding another case of a limitation makes it better of course. 并非找到另一个限制的情况当然会更好。

  3. I have not seen this my self, though this would be a step backwards if you where attempting to take code that is simple, and clean when expressed in one way and then express it in a way that is ... not that way... 我没有看到这是我的自我,尽管如果你试图采用简单的代码,并且在以一种方式表达时清洁然后以某种方式表达它......那么这将是一个倒退。 。

Rich Hickey said in one of the blip.tv lectures that Clojure is "85 % functional". Rich Hickey在其中一个blip.tv讲座中表示,Clojure的功能是“85%”。 I like to see core.async as one part of the other 15%. 我喜欢将core.async视为其他15%的一部分。 Core.async is great for solid user-interaction among other things which would have been done by promises, delays and other things, likely in a more messy way. Core.async非常适合用户之间的互动,这些事情本来可以通过承诺,延迟和其他事情来完成,可能会以更混乱的方式进行。

Every programm has two parts, one part that is always non function talk data in, spit it out and so on. 每个程序都有两个部分,一部分始终是非函数通话数据,吐出来等等。 For this part we know have core.async, it is ture that core.async has mutable things, but note two things. 对于这部分我们知道有core.async,核心.async确实有可变的东西,但请注意两件事。 The state of the channels is basiclly managed by the library. 渠道的状态基本上由图书馆管理。 The things you put on it are, what on might call flowstate. 你放在它上面的东西是什么,可能称之为flowstate。 Meaning that when you put something like a channel you do not expect to come back at it, or even change it. 这意味着,当您放置类似通道的东西时,您不希望再回到它,甚至更改它。

Core.async is nice to manage this part of your programm. Core.async非常适合管理这部分程序。 For the rest, all the transformation and calculation on your data, clojure tries its best to give you good tools to do it functionally. 对于其余的,对数据的所有转换和计算,clojure会尽力为您提供良好的工具来实现它。

It seems to me that this prevents simplicity and composability. 在我看来,这可以防止简单性和可组合性。 Why is it not a problem? 为什么不是问题?

There are two worlds, the sync and the async world. 有两个世界,同步和异步世界。 You can put stuff, or take stuff out of the async wrold with put! 你可以把东西放进去,或者用put来取出异步的东西! and take! 并采取! but other then that (and maybe some other function) these to worlds are seperated from each other. 但除此之外(也许还有一些其他功能)这些对世界来说是彼此分开的。 Clojure does not want to become a completly async language. Clojure不想成为完全异步的语言。 The functions and data transformation are what need to be composable. 功能和数据转换是需要组合的。

Maybe as a consequence of the previous two issues, a lot of code with core.async uses lower-level constructs such as loop/recur instead of map/filter/reduce. 也许作为前两个问题的结果,core.async的许多代码使用较低级别的构造,例如loop / recur而不是map / filter / reduce。 Isn't it a step backwards 这不是一个倒退

Operation like that over channels will be possible. 可以通过频道进行操作。 Core.async is still young and not all possible construct and functions have been written yet. Core.async仍然很年轻,并没有编写所有可能的构造和函数。

But in generally if you have large data transformations you dont really want to do them in async world, you want to have them in a collection and then throw something like the reducres framework at it. 但一般来说,如果你有大数据转换,你真的不想在异步世界中做它们,你想把它们放在一个集合中,然后在它上面添加类似reducres框架的东西。

The main thing to understand is this, core.async is not the new standard, its just one more thing that helps you programm. 要理解的主要内容是,core.async不是新标准,它只是帮助您编程的另一件事。 Sometimes you need STM, sometimes Agents, sometimes CSP it depends and clojure wants to give you all the options. 有时候你需要STM,有时需要代理,有时候它需要CSP,而clojure想要给你所有的选择。

One reason people like core.async is because it helps with some stuff that is otherwise really hard to handle, like dealing with callbacks. 人们喜欢core.async的一个原因是因为它有助于处理一些本来很难处理的东西,比如处理回调。

perhaps a possible solution to use (<! c) outside go macro could be done with macro and its macro expansion time : 也许一个可能的解决方案使用(<! c)外部宏可以用宏及其宏扩展时间来完成:

This is my example: 这是我的例子:

(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!")
  )

And to test it: 并测试它:

(read-channel print)
(repeatedly 50 paint)

I've tried this solution in a nested go and also works. 我已经在嵌套go中试过这个解决方案并且也可以工作。 But I'm not sure if it could be a correct path 但我不确定它是不是一条正确的道路

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM