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(带有 more-data)-> someNewThing

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

在所有情况下,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
这是真的。在这种情况下,“新的 Thing”可以使用键和值来找到——因为外键关系是基于键和值的(以及可能的模式选项),而不是找到它的数据行。

导航可能关心也可能不关心值、键甚至集合:)
特别是,coll常常是一个存放将在nav过程中需要的元数据的方便之处。
好吧,所以我得出以下结论

- `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) ...

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

我还有一些不太明白。如果你看我的例子,我假设nav返回一个数据化结果。但Sean提到它不应该这样做。但这在我例子中甚至说不通。我从一个URL开始。我可以在该URL中进行nav以获取返回的页面,但没有coll上下文。而且我可以让nav返回的不是已数据化的东西,但我没有这样的东西,并且空想一个似乎很奇怪。

这里我假设`nav`可以返回数据或其他可数据化事物,这并不重要。并且数据化将总是被调用,但如果它已经是数据,数据化函数将只返回数据,因为这是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`返回一个数据化结果。"

不,应该返回一个对象。

"我可以nav进入该URL以获取页面"

不,你首先应该数据化URL以获取数据,然后导航到该数据(这就是你的coll)。

"我可以在nav中返回不是已数据化的东西,但我没有这样的东西,为这个事情杜撰一个看起来很奇怪。"

如果你已经有数据,从nav返回它是完全可以的。但在这里,我认为你可能想要返回一个“as-url”对象,不管它是什么形式。
by
应该首先数据化URL以获取数据,然后导航到该数据(这就是你的coll)。

当你说“nav到数据”时我感到困惑,因为集合可以被导航进入,比如get、get-in、first、nth等操作所能完成的事情。为了导航到数据元素或嵌套集合中的元素。但也被视为可以导航进入的元素,如果它们是超链接的形式,那么剩余的内容需要从远程获取或渲染。

最初我以为nav可以同时做到这两件事,并把结果数据化。但现在看起来nav似乎只做后者?

如果nav只做后者,那么对于很多用例来说,它将是一个空操作,对吗?就像一个恒等函数。比如,当不需要获取或渲染任何内容,只将内容转换为数据时。比如如果你有嵌套在对象中的对象呢?

因此,我认为datafy不应该深度地执行。但这是真的吗?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对象……我们可以将其datafy以获取引用内容作为数据结构,并回到Clojure数据世界。

如果你还没有尝试过REBL,我强烈建议你去做——这确实会使其更清晰(或者至少,我_希望_它会)。
+1
by

nav是一个通用的导航框架。给定一个“容器”(集合)和在容器的某些选择,可以“导航”到该选择。对于容器,k是有意义的(比如地图),对于某些情况可能不必要,此时选择值本身就足够了。作为一个通用框架,两者都提供了,因为你可能需要它们来做选择。

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

在REBL中,nav用于支持浏览操作,即在左侧面板中选择某些内容并将其“浏览”进去。其他用途可能会执行不同的操作(序列化对象图等)。

我曾认为,在我拥有的事物中添加对datafy/nav的支持,也意味着在REBL中添加了对它的支持。是这样的吗?因为为了这个目的,我假设应该有一个“深入”的约定,比如说,当你评估某个表达式时,datafy会被应用于它,并将结果在内容面板中渲染。如果这是一个REBL可以选入的已知类型(如地图或向量),那么选择集合中某个索引并通过nav前进将会调用nav在valueied表达式上,其中k是索引,v是值,该结果将转化为新的表达式,并重复进行。

我可以看到datafy/nav可以这样使用,基本上,你可以根据自己的约定在其内部使用,但在REBL的上下文中,是否存在一个需要遵循的约定呢?

再次感谢!

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

1. 内容面板对选中的表达式调用datafy
2. 前进nav调用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的方式))。
...