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

欢迎!请查看 关于 页面以获取更多关于如何使用本站的信息。

+8
协议
retagged

函数 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来返回到Clojure数据世界,使用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可能关心或不关心值、键,甚至集合:)
特别地,在导航过程中,`coll` 通常是一个方便存储所需元数据的地方。
好的,那么我明白了...

- `v` 是我们要导航到的目标。
- `coll` 应该是调用 `datafy` 所得到的返回结果。
- `v` 应该包含在 `coll` 中,可以通过 `k` 来获取。

因此,一个使用场景可以是:

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

这将返回另一个东西(也就是任意可datafy的对象),它是从导航到 `v` 获取的。

从这个意义上说,`v` 应该是一个超链接,它实际上不是一个集合,这就是为什么我们导航到超链接指向的地方,从而返回另一个东西,我们可以随后再对其进行datafy操作,并进一步导航。

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

(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集合进行了datafy,并使用正常的Clojure集合函数进行了深入探索。当我们达到另一个可导航值时,我们调用nav来获取下一页。

唯一让我有点困惑的是,如果你看我的例子,我假设`nav`返回datafied结果。但Sean说这不应该这样。但在我的例子中这也不合理。我从URL开始。我可以导航到这个URL以获取页面,但还没有`coll`上下文。我可以让nav返回一个非数据的东西,但我没有这样的东西,随意编造一个似乎也有些奇怪。

因此,我假设`nav`可以返回数据或datafiable对象,这并不重要。同时假设无论如何都会调用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)
      概述页作为集合(datafy概述页)
  概述页

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

谢谢!
by
我原本期待nav与get匹配,而不是get-in,并且我期望它可以根据数据类型工作(因为它基于协议)。

(评论对于共享代码来说非常糟糕,所以我不会尝试,但我将基于你的URI导航构建一个例子,稍后回来说明。)
by
“我假设`nav`返回一个datafied结果。”

不,应该返回一个对象。

“我可以用nav进入那个URL并获取页面。”

不,你应该首先对URL进行datafy以获取数据,然后再nav进入那些数据(这就是你的集合)。

“我可以用nav返回不是数据的东西,但我没有这样的东西,只是为了构建一个看起来很奇怪。”

如果你已经有了数据,从nav返回它完全没有问题。但这里我认为你想要返回一个“as-url”对象,无论形式如何。
by
> No, you should first datafy the URL to get data, then nav into that data (there's your coll"/>.

当你说到nav进入数据时,我感到困惑,因为集合可以进入导航,例如get,get-in,first,nth等可以做的事情。为了导航到数据的元素或其中的嵌套集合。但如果元素是一种超链接形式,则需要远程获取和/或渲染其他内容。

最初我假设nav两者都做,甚至datafied结果。但现在我认为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将其转换为java.net.URL对象。

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

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

如果你还没有尝试REBL,我强烈建议你试试,这真的会让这一切变得更加清晰(至少,我希望如此)。
+1

nav是一个通用的导航框架。给定一个“容器”(集合)和该容器中的某些选择,导航到该选择。对于容器,k有意义(如映射),对于某些(如序列),选择值本身就足够了。作为一个通用框架,两者都提供给你,因为你可能需要两者来完成选择。

至于输入和输出究竟是什么,这是一个完全上下文相关的答案。nav是一个符合特定目的的工具,因此不知道特定目标,就无法具体回答。你通常是nav的调用者和实现者(通过实例元数据),重要的是它们要一致。

在REBL中,"nav"被用于浏览操作中,其中你已经在左侧面板中选择了一些内容,然后"浏览"进入它。其他用法可能会做不同的操作(例如序列化对象图等)。

我一直以为给我的东西添加对datafy/nav的支持,将会在REBL中添加对它的支持。是这样的吗?因为为此,我曾假设应该有一个"深入挖掘"的约定,比如说,当你评估某个表达式时,datafy会对其调用,并在内容面板中渲染结果,如果这是一个REBL可以选中的已知名类型,例如map或vector,那么在coll中选择一个idx并向前导航就会在datafied表达式上调用nav,其中`k`是idx,`v`是val,然后这个结果将成为新的表达式,并以此类推。

我可以理解datafy/nav可以多么通用,基本可以使用它来遵循你自己的约定,但在REBL的上下文中,是否有这样的约定可以遵循呢?

再次感谢!
我实际上尝试了在REBL中这样做,看起来是这样的

1. 内容面板对选定的表达式调用datafy
2. 前进导航调用nav,其中`coll`是第1步datafy的返回值
`k`是从内容面板选择的`idx`
`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的情况是nav在哈希表上的默认行为仅仅返回值v(也就是get coll k,因为这就是你在这个上下文中调用nav的方式)。
...