解决Clojure循环依赖关系

mik*_*era 15 clojure circular-dependency

我的工作有不同的命名空间之间的一些循环依赖一些Clojure的代码,我试图找出解决这些问题的最佳途径.

  • 基本问题是我在其中一个文件中得到"No such var:namespace/functionname"错误
  • 我试图"声明"该函数,但后来抱怨:"不能引用不存在的合格var"
  • 我当然可以重构整个代码库,但是每次你有一个依赖关系来解决这个问题似乎都是不切实际的.....并且对于某些循环依赖的网络可能会变得非常难看
  • 我可以将一堆接口/协议/声明分离到一个单独的文件中,并且所有内容都引用了....但是这似乎最终会变得混乱并破坏我所拥有的相关功能的当前漂亮的模块化结构一起

有什么想法吗?在Clojure中处理这种循环依赖的最佳方法是什么?

Mic*_*zyk 24

我记得Clojure中有关命名空间的一些讨论 - 在邮件列表和其他地方 - 我必须告诉你,共识(和AFAICT,当前Clojure设计的方向)是循环依赖是一个设计的呼声重构.偶尔可能会有变通方法,但是丑陋,可能会对性能造成问题(如果你让事情变得不必要"动态"),不能保证永远工作等等.

现在你说循环项目结构很好,模块化.但是,如果一切都依赖于一切......为什么你会这么称呼呢?此外,如果您提前计划树状依赖结构,那么"每次有依赖性来解决"都不应该经常发生.为了解决将一些基本协议等放在自己的命名空间中的想法,我不得不说很多时候我都希望项目能够做到这一点.我发现它有助于我浏览代码库并快速了解它正在使用什么类型的抽象.

总而言之,我的投票是重构.

  • 一个小小的更新 - 将协议放在他们自己的命名空间中运行良好并解决了大多数问题,我通常最终添加一个(:use [protocols])到大多数其他ns声明和一切"正常".我仍然觉得难以解决的唯一问题是你声明一个类(例如deftype),你想在它被声明之前引用它(例如作为协议定义中的类型提示!!) (2认同)

Ham*_*aya 14

我有一些类似gui代码的问题,我最终做的是,

(defn- frame [args]
  ((resolve 'project.gui/frame) args))
Run Code Online (Sandbox Code Playgroud)

这允许我在运行时解析调用,这是从帧中的菜单项调用,所以我100%确定帧是定义的,因为它是从帧本身调用的,请记住,resolve可能返回nil.


Ral*_*och 9

我经常遇到同样的问题.尽管许多开发人员不愿意承认这一点,但这是该语言中一个严重的设计缺陷.循环依赖是真实对象的正常条件.没有心脏,身体就无法生存,没有身体,心脏就无法生存.

可以在呼叫时解决,但这不是最佳的.假设你有一个API,作为api的一部分是错误报告方法,但是api创建了一个有自己方法的对象,这些对象需要报告错误,你有循环依赖.错误检查和报告功能将经常被调用,因此在调用它们时解析不是一种选择.

在这种情况下,大多数情况下,解决方案是将没有依赖关系的代码移动到可以自由共享的独立(util)命名空间中.我还没遇到过用这种技术无法解决问题的情况.这使得维护完整,功能性的业务对象几乎不可能,但它似乎是唯一的选择.Clojure还有很长的路要走,它是一种能够精确建模现实世界的成熟语言,直到那时以不合逻辑的方式划分代码才是消除这些依赖关系的唯一方法.

如果Aa()依赖于Ba()和Bb()依赖于Ab(),唯一的解决方案是将Ba()移动到Ca()和/或Ab()移动到Cb(),即使C在技术上不存在于现实中.

  • 身体和心脏不是由可组合构成或设计的.命名空间应该是.你不仅仅通过"模拟现实世界"来获得可组合性. (2认同)
  • 命名空间存在的唯一目的是能够在不同的上下文中重复使用相同的名称而不发生冲突。通过对现实世界进行建模,您得到的是直观且可维护的设计。我不会质疑心脏或身体的可组合性,但有很多案例表明它们确实是可组合的。 (2认同)
  • lgrapenthin,我读了Hickey的评论,虽然他清楚地说明了lisp风格的一些优点,但他只是在找借口.第三方命名空间的声明(如果在定义之前调用会导致错误)和更软的加载规则(例如soft-require,它表示需要该功能但不触发文件加载)解决了每个问题Hickey哭了关于.纯粹缺乏Hickey的经验. (2认同)