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 -> 更多数据

description-hash-map -> nav(使用更多数据)-> someNewThing

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

在所有情况下,nav可能关心也可能不关心值参数(实际上,它可能还可能不关心键参数……但是可能性较小)。请注意,当datafy产生某些对象的表示形式时,它在数据表示上会添加原始对象作为元数据,并且该元数据可以通过coll参数提供给nav,以便在需要时访问原始对象。

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

通过
我确实查看了next.jdbc的实现,并在nav的实现中从未使用过coll:https://github.com/seancorfield/next-jdbc/blob/1b93d3a04ba033bb627e493df28f32330dfeb2e5/src/next/jdbc/result_set.clj#L643-L664

这让我更加困惑:)
通过
是的。在这种情况下,可以通过仅使用键和值来找到“新的东西”,因为外键关系是基于键和值的(以及可能的schema选项),而不是该值所在的数据行。

所以nav可能关心,也可能不关心值、键,甚至整个集合:)
by
特别地,在导航过程中存储将在导航中需要的元数据的集合(coll)通常是非常方便的。
by
好吧,所以我总结出来

- `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 的 datafy 实现方式,我们可以假设我们得到以下结果

{: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)
      概述页面作为coll(将概述页面数据化)
  概述页面)

这是正确的吗?或者你应该实际上从`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)]
  概述页面)

谢谢!
我期望nav与get匹配,而不是get-in,并且我期望它是基于数据类型的(因为它基于协议)。

(注释对于共享代码非常糟糕,所以我不会尝试,但我会基于你的URI导航创建一个例子,稍后再回复。)
"我假设`nav`返回的是数据化的结果。"

不,应该返回一个对象。

"我可以用nav进行导航以获取该页面。"

不,你应该先对URL进行数据化以获取数据,然后再在该数据中nav导航(那里是你的coll)。

"我也可以让nav返回不是已经数据化的东西,但我没有这样的东西,因此为了这样做而随机地编写它感觉有点奇怪。"

如果你已经有了数据,从nav返回它是完全可以的。但这里我认为你需要返回一个"as-url"对象,无论它是什么形式。
> 不,你应该首先将URL数据进行数据化,然后再在该数据中nav导航(那里是你的coll)。

当你说到nav到数据时,我有点困惑,因为可以导航到集合,例如get、get-in、first、nth等可以做的那样。为了导航到数据元素或其中的嵌套集合。但也可以导航到元素,如果它们是超链接的形式,那么其余的内容需要远程检索并渲染。

最初我假设nav可以进行两项操作,并且还将结果数据化。但现在看来,nav似乎只应该进行后者?

如果nav只进行后者,那么将它用于许多用例都是正确的,比如进行一个恒等函数。在不需要获取或渲染任何内容,但只需要转换为数据的情况下,比如当你有嵌套在对象中的对象时?

对于这一点,我认为datafy不应该进行深度处理。但这真的是对的吗?应该深度递归地对整个对象及其包含的所有对象进行数据化,除了在需要远程获取更多数据或进行渲染的情况下?
by
实体世界 -> 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
by

nav是一个通用的导航框架。给定一个"容器"(集合)和其中的某些选择,"导航"到该选择。对于集合,k是有意义的(例如,像映射一样),对于某些容器,它是多余的,选择值就足够了。作为一个通用框架,两者都提供给你,因为你可能需要两者来进行选择。

至于输入和输出的具体内容,这完全取决于上下文。nav是一个适用于特定目的的工具,不知道特定的目标,就无法具体说明。你通常是nav的调用者和实现者(通过元数据实例),重要的是它们匹配。

在REBL中,nav用于浏览操作的服务中,假设你在左侧面板中选中了一些内容,然后将它进行浏览。其他的使用可能执行不同的任务(比如序列化对象图等)。

我有一种印象,就是在我个人拥有的东西中添加支持datafy/nav可能会在REBL中添加支持。这不是这样的吗?因为为了这个,我假设应该有一套“深入挖掘”的约定,比如说,当你评估一个表达式时,datafy会被应用在其上,并且结果在内容面板中显示,如果这是一个可以被REBL选择的已知类型(比如地图或向量),那么在集合中选中索引并向前浏览会调用nav在datafied表达式上,其中`k`是索引,`v`是值,并且结果会变成新的表达式,并重复进行。

我可以看出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 时返回的默认行为就是值 v(因为正是通过这种方式调用 nav 的)。
...