请在 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 世界中的等效点来返回到 Clojure 数据世界。

myThing -> datafy -> description-hash-map

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

description-hash-map -> nav (with more-data) -> someNewThing

如果 datafy 产生了一个向量,你可以带上这个向量、索引(向量在它们的索引上是关联的)以及该索引处的元素来调用 nav

在所有情况下,nav 可能或可能不会关心值参数(实际上,它可能或可能不会关心键参数...但这种情况不太可能)。请注意,当 datafy 产生某些对象的数据表示时,它将原始对象作为元数据添加到数据表示中 - 并且可以通过 coll 参数提供给 nav,以便它可以访问原始对象(如果需要的话)。

如果您想在现实中看到一个具体示例,请参阅 next.jdbc 的 datafynav:schema 机制

by
我查看了 next.jdbc 的实现,发现 collnav 的实现中从未被使用:https://github.com/seancorfield/next-jdbc/blob/1b93d3a04ba033bb627e493df28f32330dfeb2e5/src/next/jdbc/result_set.clj#L643-L664

这让我更加困惑 :(
by
确实如此。在这种情况下,可以通过键和值找到“新的 Thing”——因为外键关系是基于键和值的(以及可能的模式选项),而不仅仅是发现它的数据行。

所以 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集合。我们使用Clojure的正常集合函数深入其中。当我们达到另一个可导航值时,我们调用nav来获取下一页。

唯一让我有些困惑的是。如果你看看我的示例,我假设nav返回一个数据化的结果。但Sean提到它不应该这样做。但这在我的示例中甚至没有意义。我开始于一个URL。我可以导航到这个URL以获取页面,但还没有`coll`上下文。并且我可以让nav返回一个非数据的对象,但我没有这种东西,仅为此而创建一个似乎很奇怪。

所以我假设在这里`nav`可以返回数据或数据化的事物,这并不重要。而且假设无论是什么都会调用datafy,不管它是否已经是数据,因为那是对Map的默认实现。

所以你会继续这样做

(let [url (as-url "https://clojure.org")]
      clojure-page-as-coll (datafy url)
      overview-url (get-in coll [:links :overview])
      概述页面(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` 返回一个数据化的结果。"

不,应该返回一个对象。

"我可以通过 nav 进入那个 URL 来获取页面回退"

不,你首先应该将 URL 数据化以获取数据,然后进入这些数据(这就是您的集合)。

"而且,我可以让 nav 返回的不是已经数据化的东西,但我没有这样的东西,只是为了构建一个看起来很奇怪。"

如果您已经有数据,那么从 nav 返回它完全可以。但在这里,我认为您想返回一个 "as-url" 对象,无论它采用什么形式。
by
> 不,你应该首先将 URL 数据化以获取数据,然后进入这些数据(这就是您的集合)。

当您说 nav 进入数据时,我会感到困惑,因为集合可以被进入,例如 get、get-in、first、nth 等可以做的那样。为了导航到数据的元素或其内部的嵌套集合。但如果是超链接的类型,那么元素也可以被进入,因为其余的内容需要远程获取或渲染。

最初我认定nav既负责导航又负责数据处理,甚至还把结果数据化了。但现在看起来,nav似乎只需要做后者?

如果nav只负责后者,那么就会有很多场景,其中它的作用将是无效的,对吧?就像一个恒等函数。例如,不需要检索或渲染任何内容,而只是将内容转换为数据。例如,如果有嵌套在对象中的对象呢?

因此,我假设datafy不应该深入地处理数据。但这句真的吗?它应该深度递归地对整个对象及其所有包含的对象进行处理吗?除非需要远程检索更多信息或渲染?
by
从现实世界 -> datafy -> Clojure数据世界

Clojure数据世界 -> 获取等 -> Clojure数据世界

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

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

datafy / nav 其中之一(或两者)可以执行某些复杂的转换以转换“世界”。

例如,(datafy (java.net.URL. "...")) 可以“转换”Java对象为URL的内容,并以数据形式渲染。

现实世界 -> datafy -> Clojure数据世界

在那份数据中,Clojure导航可以让你访问页面中的每个链接(以代表它的Clojure数据结构的形式,如{:link "..."})。

Clojure数据世界 -> 获取等 -> Clojure数据世界

然后可以在这些相同的路径上应用nav,将{:link "..."}转换为java.net.URL对象。

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

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

如果你还没有尝试REBL,我强烈建议你尝试一下——它真的会把这些事情解释得更清楚(至少,我希望是这样的)。
+1
by

nav是一个通用的导航框架。给定一个“容器”(集合)和一些在该容器中的选择,将“导航”到该选择。对于容器,诸如k(如map)等是有意义的;对于其他一些情况,它是多余的,选择值本身就足够了。作为一个通用框架,这两者都提供给你,因为你可能需要它们来进行选择。

关于输入和输出到底具体是什么,这是一个完全依赖于上下文的答案。nav是一种适用于特定目的的工具,因此不知道一个特定的目标,就不可能具体说明。您通常是nav的调用者和实现者(通过实例元数据),重要的是它们要匹配。

在REBL中,nav用于支持浏览操作,其中您在左侧面板中选择了某些内容,然后进入浏览它。其他用法可能执行不同的操作(序列化对象图等)。

我曾以为将支持datafy/nav添加到我所拥有的某个应用程序中即可在REBL中实现支持。难道不应该是这样吗?因为如此,我假设应该有一个“深入探讨”的约定,比如说当你求值某个表达式时,datafy会被应用于它,并将结果渲染在内容窗格中,如果是REBL可以选入的已知类型,如map或vector,则选择集合中的idx并在导航下前进会以`k`为idx,`v`为val调用datafy表达式,然后这个结果将成为新的表达式,并重复此操作。

我明白datafy/nav可以非常通用,基本上可以在你自己的约定中使用它,但在REBL的上下文中,是否有遵循的约定呢?

再次感谢!

编辑了
实际上,我尝试在REBL中这样做了,看起来是这样的

1. 内容窗格调用datafy于所选的expr
2. 导航前进调用nav,其中`coll`是第1步中datafy的返回值
3. `k`是从内容窗格中选择的`idx`
4. `v`是从内容窗格中选择的`val`

从这一点及这里所有的回答中,我认为如下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在hash map上的默认行为是只返回值v(因为这就是在这个上下文中调用nav的方式))。
...