【问题标题】:Precedence of clojure protocols in another namespace另一个命名空间中 clojure 协议的优先级
【发布时间】:2016-03-30 15:08:08
【问题描述】:

在一个使用 clojure.java.jmx 的项目中,我扩展了它的 Destract 协议 objects->data 函数,以将更多从调用或元数据查询返回的 JMX 数据结构转换为普通的 clojure 数据结构。

当我处理完单个数据结构后,应该可以使用(walk/prewalk jmx/objects->data (jmx/operations "java.lang:type=Threading"))

但是,在Destract 协议中,objects->data 函数的implementation 类型为clojure.lang.Associative,这意味着映射将被错误地处理。 我可以在我的命名空间中为clojure.lang.IPersistentMap 添加一个实现,但由于clojure.lang.Associative 也是地图的一个接口,所以这不起作用。

因此,我最终不得不分叉 clojure.java.jmx。如果有办法改变偏好,或者为另一个命名空间中的类型撤回协议,我就不必这样做了。

有没有办法防止clojure.lang.Associative 在协议中优先于clojure.lang.IPersistentMap

如果没有,是否可以为另一个命名空间中的类型撤回协议?是否有可能在协议编译成 Java 接口的方式上实现它?

【问题讨论】:

    标签: clojure namespaces clojure-protocol


    【解决方案1】:

    它应该可以工作

    您确定为clojure.lang.IPersistentMap 提供您自己的实现不起作用吗?它适用于我的 REPL 会话。当我只是覆盖clojure.lang.Associative 的默认实现时,它甚至可以工作:

    user=> (ns ns1)
    ;;=> nil
    
    ns1=> (defprotocol IPrintable (prnt [this]))
    ;;=> IPrintable
    
    ns1=> (extend-protocol IPrintable clojure.lang.Associative (prnt [this] (str "clojure.lang.Associative " this)))
    ;;=> nil
    
    ns1=> (ns ns2)
    ;;=> nil
    
    ns2=> (ns1/prnt {:a 1})
    ;;=> "clojure.lang.Associative {:a 1}"
    
    ns2=> (extend-protocol ns1/IPrintable clojure.lang.Associative (prnt [this] (str "My custom impl for clojure.lang.Associative " this)))
    ;;=> nil
    
    ns2=> (ns1/prnt {:a 1})
    ;;=> "My custom impl for clojure.lang.Associative {:a 1}"
    

    它也适用于sample project

    你必须记住requireextend-protocol所在的命名空间,如果它没有被另一个命名空间传递加载。重要的是,您的 extend-protocol 将在您要覆盖的那个之后加载。

    在内部 extend-protocolextend-type 修改附加到协议元数据的特殊映射,其中 assocs 具有函数实现的给定类型。如果给定类型存在现有条目,它将被覆盖。因此,extend-* 的执行顺序很重要。

    当它不起作用时

    当一个对象直接实现接口或协议时(例如,defrecord 中的内联),您不能覆盖实现(继续 REPL 会话):

    ns2=> (defrecord SomeData []
            ns1/IPrintable (prnt [this] "I'm SomeData"))
    ;;=> ns2.SomeData
    
    ns2=> (ns1/prnt (SomeData.))
    ;;=> "I'm SomeData"
    
    ns2=> (extend-protocol ns1/IPrintable SomeData (prnt [this] "Custom impl " this))
    ;;=> IllegalArgumentException class ns2.SomeData already directly implements interface ns1.IPrintable for protocol:#'ns1/IPrintable  clojure.core/extend (core_deftype.clj:775)
    

    【讨论】:

    • 问题是当遇到映射时,Associative 函数优先于 IPersistentMap 函数,因为映射两者兼而有之。多亏了这个答案,我才发现我可以用我的命名空间调用标识中的一个来覆盖关联函数,使其成为一个 noop。有点hacky,但最终实际上与删除它相同。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-07-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-21
    • 1970-01-01
    相关资源
    最近更新 更多