2024年Clojure状态调查!中分享您的想法。

欢迎!请参阅关于页面了解更多关于这是如何工作的信息。

+8
协议
重新标签

clojure.protocols/nav 函数的签名是

[coll k v]

我不理解collkv应该是什么。有人能解释吗?也许可以给出一个使用实际参数调用nav的示例?

比如,什么是coll。它是先前调用datafy的结果吗?因此coll是我们将要导航到的东西,还是那是v

我假设k是钻探所需的信息。

现在有v,所以我又不确定这意味着我们打算导航到我们想导航的值。如果是这样,那么coll是做什么的?如果v不是这样,那我就不明白它应该是什么了。值不是我们要nav函数返回的吗?为什么我们还要提供它?

谢谢

2 个答案

+6

让我们先假设我们有一个类型为Thing的对象,并且它有一个实现datafy的对象,它产生一个描述该事物的Clojure哈希表。

myThing -> datafy -> description-hash-map

现在我们可以使用 get 来遍历这个哈希表:(get description-hash-map :foo),这将生成我们称之为 foo-val 的某些值。

通过 datafyThing 世界转换到 Clojure 数据世界,我们可以使用所有常规的 Clojure 函数在 数据世界 中导航,但我们在表示为数据世界中的元素的点通常需要一个方法回到 Thing 世界。

这就是 nav 的作用所在。对于任何产生 foo-val 值的 (get description-hash-map :foo),我们可以通过调用 (nav description-hash-map :foo foo-val) 来回溯到 Thing 世界中的对等点,这样就会在 Thing 世界中产生一些新的对象。

然后,我们可以在新对象上调用 datafy,使用 get 在数据世界中导航,使用 nav 回到 Thing 世界中的对等点。

myThing -> datafy -> description-hash-map

description-hash-map -> get -> more-data

description-hash-map -> nav(带有 more-data) -> someNewThing

如果 datafy 生成一个向量,请调用 nav 并携带该向量,索引(向量在其索引上是关联的),以及该索引处的元素。

在任何情况下,nav 可能关注或可能不关注值参数(实际上,它可能也不关注键参数...但可能性较小)。请注意,当 datafy 生成某些对象的日期表示时,它将原始对象添加为数据表示上的元数据 -- 并且这个元数据通过 coll 参数可供 nav 使用,以便在需要时获取原始对象。

如果您想在真实世界中的具体例子中查看,请参阅next.jdbc的 datafynav:schema 机制

by
我已经查看过 next.jdbc 的实现,但在 nav 的实现中从未使用过 collhttps://github.com/seancorfield/next-jdbc/blob/1b93d3a04ba033bb627e493df28f32330dfeb2e5/src/next/jdbc/result_set.clj#L643-L664

这使我更加困惑 :(
by
是的。在这种情况下,只需要使用键和值就可以找到“新事物”,因为外键关系是基于键和值(以及可能的模式选项),而不是数据所在的行。

所以nav可能关心或不关心值、键甚至集合:)
在nav过程中,存储所需元数据,“coll”通常是一个方便的地方。
好吧,那么我明白了

- `v` 是我们要导航进去的内容。
- `coll` 应该是`datafy`调用后的结果
- `v` 应该包含在`coll`中,可以通过`k`检索

因此,一个使用场景可能是

(let [coll (datafy some-obj)
      k :next-thing
      v (get coll k)]
  ;(nav coll k v))

这将返回另一个事物(即任意可数据化的对象),这是从导航到`v`中获得的。

从这个意义上讲,`v`应该是一个超链接,它实际上不是一个集合,这就是为什么我们导航到超链接所指的地方,这将返回另一个我们可以稍后数据化的事物,并可能进一步导航。

所以一个完整的例子可能是

(let [url (as-url "https://clojure.org")
      clojure-page-as-coll (datafy url) ...

在这一步,根据URL的数据化实现,我们可以假设我们得到了以下结果

{:content
 :links {:get-started (as-url "https://clojure.org/guides/getting_started")
         :overview (as-url "https://clojure.org/about/rationale")
         ...}
...}

因此,如果我们想导航到[:links :overview],我们会这样做

(let [url (as-url "https://clojure.org")
      clojure-page-as-coll (datafy url)
      overview-url (get-in coll [:links :overview])
      overview-page (nav clojure-page-as-coll [:links :overview] overview-url) ...

所以到这个点,我们已将一个URL作为Clojure coll数据化了。我们使用正常的Clojure集合函数深入挖掘它。当我们到达另一个可导航值时,我们在它上面调用nav以获取下一页。

我还有一点不太清楚。如果你看我的示例,我假设 `nav` 会返回一个数据化结果。但Sean提到它不应该这样。但这在某些情况下在我的示例里是不成立的。我开始从一个URL开始。我可以导航到这个URL来获取页面,但没有`coll`上下文。而且我无法让nav返回不是数据的东西,我没有这样的东西,只是随便造成一个似乎很奇怪。

所以我这里假设`nav`可以返回数据或数据化东西,这实际上并不重要。并且假设无论是什么都被调用了`datafy`,但如果它已经数据化了,datafy会直接返回数据,因为这是Map的默认实现。

所以你会继续这样做

(let [url (as-url "https://clojure.org")
      clojure-page-as-coll (datafy url)
      overview-url (get-in coll [:links :overview])
      overview-page (nav clojure-page-as-coll [:links :overview] overview-url)
      overview-page-as-coll (datafy overview-page)]
  overview-page)

这是正确的吗?或者你实际上应该以 `nav` 开头?也许我的示例不好,因为在这里`datafy`显得是多余的,因为某人可以这样做了

(let [clojure-page (nav nil nil (as-url "https://clojure.org"))
      overview-url (get-in clojure-page [:links :overview])
      overview-page (nav clojure-page [:links :overview] overview-url)]
  overview-page)

谢谢!
by
我以为nav会匹配get,而不是get-in,并且我认为它应该根据数据类型(因为它基于协议)来工作。

(评论不适合分享代码,所以我不尝试,但是我将基于你的URI导航构建一个示例,稍后再回来)
by
"我假设 `nav` 返回一个 datafied 结果。"

不,应该返回一个对象。

"我可以 nav 到这个 URL 来获取页面回传"

不,你应该首先将 URL 数据化来获取数据,然后 nav 到那些数据(那里是你的 coll)。

"并且我可以让 nav 返回的东西不是已经数据化的,但我没有这样的东西,只是随便造成一个似乎很奇怪。"

如果你已经有了数据,让它从 nav 中返回是完全可以的。但在这里我认为你想返回一个“as-url”对象,无论那是什么形式。
> 不,你应该首先将URL数据化以获取数据,然后导航到该数据(这就是你的集合)。

当你说导航到数据时,我感到困惑,因为集合可以被导航,例如"get"、"get-in"、"first"、"nth"等可以做的那样。为了导航到数据的元素或其内部的嵌套集合。但是,如果它们是以超链接的形式导航,则需要远程获取或渲染其余内容。

最初我以为nav可以同时做到这两者,并且还对结果进行了数据化。但现在看来nav似乎只应该做后者?

如果nav只做后者,那么对于它无用的用例将有很多,对吗?就像一个恒等函数。例如,在不需要获取或渲染,只需要将数据转换为数据的情况下。例如,如果你有嵌套在对象中的对象呢?

对于这种情况,我想datafy不会太深。但这真的对吗?应该深度和递归数据化整个对象以及所有包含的对象,除非需要远程获取更多信息或进行渲染?
实体世界 -> datafy -> Clojure数据世界

Clojure数据世界 -> get等 -> Clojure数据世界

Clojure数据世界 -> nav -> 实体世界

这里重要的是“世界”的转换。

datafy / nav中的任何一个都可以执行某些复杂转换以转换世界。

例如,(datafy (java.net.URL. "..."))可以将Java对象转换为URL的内容,以数据的形式呈现。

实体世界 -> datafy -> Clojure数据世界。

在该数据中的Clojure导航可以让你访问页面上的所有链接(如代表它的Clojure数据结构{:link "..."})。

Clojure数据世界 -> get等 -> Clojure数据世界

然后将nav应用于这些路径,将这些{:link "..."}转换为java.net.URL对象。

Clojure数据世界 -> nav -> 实体世界

现在我们又回到了实体世界,有一个java.net.URL对象...我们可以将其数据化以获取所引用的内容作为数据结构,并返回Clojure数据世界。

如果你还没有尝试REBL,我强烈推荐你试试——它真的会使这一切更加清晰(至少我希望能如此)。
+1 投票

nav 是一个通用的导航框架。给定一个“容器”(coll)和该容器内的某些选择,导航到该选择。对于容器,k 有意义(例如地图),对于某些情况,k 是不必要的,选择值已经足够。作为一个通用框架,两者都提供给你,因为你可能需要两者来完成选择。

至于输入和输出的具体内容,这是一个完全根据上下文回答。nav 是一个适合当前目标的工具,如果没有知道特定的目标,就无法具体说明。通常你既是 nav 的调用者也是实现者(通过实例元数据),重要的是它们是一致的。

在 REBL 中,nav 被用于支持浏览动作,当你在左侧面板中选中某些内容并“浏览”进去时。其他用途可能会有不同的效果(序列化对象图,等等)。

我以为将 datafy/nav 的支持添加到我所拥有的东西中,就会在 REBL 中添加对其的支持。不是这样吗?因为为此,我曾经假设应该有一个“深入挖掘”的约定,这样当你在 eval 某个表达式时,datafy 会对其调用,并将结果渲染在内容面板中。如果它是一个 REBL 可以选中的已知类型,例如地图或向量,那么通过在 coll 中选择 idx 并向前导航,将调用 nav,其中 `k` 是 idx,`v` 是 val,然后这个结果就会变成新的表达式,并重复进行。

我可以看到 datafy/nav 可以非常通用,基本上可以根据你的约定内使用它,但在 REBL 的上下文中,有没有可以遵循的约定呢?

再次谢谢!

编辑
我实际上曾用 REBL 尝试了这个功能,看起来像这样

1. 内容面板在所选表达式上调用 datafy
2. 前进导航调用 nav,其中 `coll` 是从 1 中返回的 datafy
3. 而 `k` 是从内容面板中选择的 `idx`
4. 而 `v` 是从内容面板中选择的 `val`

根据这些信息以及这里的所有回答,我觉得这个 gist https://gist.github.com/didibus/2a2a62d365f93d55db4fb27f46ecef89 似乎是对 datafy/nav 的合理使用?
在你的示例中不需要 nav: https://github.com/seancorfield/datafy-nav-example/blob/master/src/datafy_test/alt.clj

这产生了与不实现 nav 相同的确切结果(因为 nav 在哈希映射上的默认行为仅仅是返回值 v(即(get coll k),因为这是在这种情况下调用 nav 的方式))。
...