当操纵不可变的数据结构时,Clojure的assoc-in和Haskell的镜头有什么区别? [英] When manipulating immutable datastructures, what's the difference between Clojure's assoc-in and Haskell's lenses?

查看:138
本文介绍了当操纵不可变的数据结构时,Clojure的assoc-in和Haskell的镜头有什么区别?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要操纵和修改深层嵌套的不可变集合(地图和列表),我想更好地理解不同的方法。这两个库解决或多或少同样的问题,对吧?他们有什么不同,一种方法更适合于哪种类型的问题?

I need to manipulate and modify deeply nested immutable collections (maps and lists), and I'd like to better understand the different approaches. These two libraries solve more or less the same problem, right? How are they different, what types of problem is one approach more suitable for over the other?

Clojure的 assoc-in

Haskell的镜头

推荐答案

Clojure的 assoc-in 允许您使用整数和关键字指定嵌套数据struture的路径,在那条路径。它有合作伙伴 dissoc-in get-in 更新

Clojure's assoc-in lets you specify a path through a nested data struture using integers and keywords and introduce a new value at that path. It has partners dissoc-in, get-in, and update-in which remove elements, get them without removal, or modify them respectively.

透镜是双向编程的一个特定概念,您可以指定两个数据源之间的链接并且该连接允许您反映从一个到另一个的转换。在Haskell中,这意味着你可以创建镜头或类似镜头的值,将整个数据结构连接到它的某些部分,然后使用它们将变化从部分传递到整个。

Lenses are a particular notion of bidirectional programming where you specify a linkage between two data sources and that linkage lets you reflect transformations from one to the other. In Haskell this means that you can build lenses or lens-like values which connect a whole data structure to some of its parts and then use them to transmit changes from the parts to the whole.

这里有一个比喻。如果我们看一下 assoc-in 的用法,就像

There's an analogy here. If we look at a use of assoc-in it's written like

(assoc-in whole path subpart)

,我们可以通过思考 path 作为镜头, assoc-in 作为镜头组合器。以类似的方式,你可以写(使用Haskell lens 包)

and we might gain some insight by thinking of the path as a lens and assoc-in as a lens combinator. In a similar way you might write (using the Haskell lens package)

set lens subpart whole

以便我们连接 assoc-in 设置路径镜头。我们还可以完成表格

so that we connect assoc-in with set and path with lens. We can also complete the table

set          assoc-in
view         get-in
over         update-in
(unneeded)   dissoc-in       -- this is special because `at` and `over`
                             -- strictly generalize dissoc-in






这是一个相似之处的开始,但也有一个巨大的不同。在许多方面, lens 比Clojure函数的 * - in 家族更通用。通常这是Clojure的一个非问题,因为大多数Clojure数据存储在由列表和字典组成的嵌套结构中。 Haskell非常自由地使用更多的自定义类型,它的类型系统反映了关于它们的信息。透镜概括了 * - in 函数系列,因为它们在更复杂的领域中工作顺畅。


That's a start for similarities, but there's a huge dissimilarity, too. In many ways, lens is far more generic than the *-in family of Clojure functions are. Typically this is a non-issue for Clojure because most Clojure data is stored in nested structures made of lists and dictionaries. Haskell uses many more custom types very freely and its type system reflects information about them. Lenses generalize the *-in family of functions because they works smoothly over that far more complex domain.

让我们在Haskell中嵌入Clojure类型,并写入 * - in 系列函数。

First, let's embed Clojure types in Haskell and write the *-in family of functions.

type Dict a = Map String a

data Clj 
  = CljVal             -- Dynamically typed Clojure value, 
                       -- not an array or dictionary
  | CljAry  [Clj]      -- Array of Clojure types
  | CljDict (Dict Clj) -- Dictionary of Clojure types

makePrisms ''Clj

现在我们可以直接使用 set 作为 assoc-in

Now we can use set as assoc-in almost directly.

(assoc-in whole [1 :foo :bar 3] part)

set ( _CljAry  . ix 1 
    . _CljDict . ix "foo" 
    . _CljDict . ix "bar" 
    . _CljAry  . ix 3
    ) part whole

这有点明显地有更多的语法噪声,但它表示对数据类型中的路径意味着更高的清晰度,特别是它表示我们是否下降到数组或字典。如果我们想要,可以通过在Haskell类型类 Ixed 中实例化 Clj 来消除一些额外的噪声,

This somewhat obviously has a lot more syntactic noise, but it denotes a higher degree of explicitness about what the "path" into a datatype means, in particular it denotes whether we're descending into an array or a dictionary. We could, if we wanted, eliminate some of that extra noise by instantiating Clj in the Haskell typeclass Ixed, but it's hardly worth it at this point.

相反,要点是 assoc-in 适用于一种非常特殊的数据下降。它比我上面提到的类型更一般,因为Clojure的动态类型和重载 IFn ,但是一个非常类似的固定结构可以嵌入Haskell稍微努力。

Instead, the point to be made is that assoc-in is applying to a very particular kind of data descent. It's more general than the types I laid out above due to Clojure's dynamic typing and overloading of IFn, but a very similar fixed structure like that could be embedded in Haskell with little further effort.

镜头可以更进一步,但更大的类型安全。例如,上面的例子实际上不是一个真正的镜头,而是一个棱镜或遍历,它允许类型系统静态地标识不能进行遍历的可能性。这将迫使我们考虑这样的错误情况(即使我们选择忽略它们)。

Lenses can go much further though, and do so with greater type safety. For instance, the example above is actually not a true "Lens" but instead a "Prism" or "Traversal" which allows the type system to statically identify the possibility of failing to make that traversal. It will force us to think about error conditions like that (even if we choose to ignore them).

重要的是,我们可以确定当我们有一个 true 透镜,数据类型下降不能失败,这种保证在Clojure中是不可能的。

Importantly that means that we can be sure when we have a true lens that datatype descent cannot fail—that kind of guarantee is impossible to make in Clojure.

我们可以定义自定义数据类型,

We can define custom data types and make custom lenses which descend into them in a typesafe fashion.

data Point = 
  Point { _latitude  :: Double
        , _longitude :: Double
        , _meta      :: Map String String }
  deriving Show

makeLenses ''Point

> let p0 = Point 0 0
> let p1 = set latitude 3 p0
> view latitude p1
3.0
> view longitude p1
0.0
> let p2 = set (meta . ix "foo") "bar" p1
> preview (meta . ix "bar") p2
Nothing
> preview (meta . ix "foo") p2 
Just "bar"

dimensions :: Lens Point Double

> let p3 = over dimensions (+ 10) p0
> get latitude p3
10.0
> get longitude p3
10.0
> toListOf dimensions p3
[10.0, 10.0]

甚至目标模拟子部分

eulerAnglePhi   :: Lens Point Double
eulerAngleTheta :: Lens Point Double
eulerAnglePsi   :: Lens Point Double






广义地说,Lenses概括了Clojure * - in 函数家族中的整个值和子部分之间基于路径的交互类型。你可以在Haskell中做更多,因为Haskell有一个更加发达的类型和镜头的概念,作为第一类对象,广泛推广获取和设置的概念,简单地用 * - / code>函数。


Broadly, Lenses generalize the kind of path-based interaction between whole values and subparts of values that the Clojure *-in family of functions abstract. You can do a lot more in Haskell because Haskell has a much more developed notion of types and Lenses, as first class objects, widely generalize the notions of getting and setting that are simply presented with the *-in functions.

这篇关于当操纵不可变的数据结构时,Clojure的assoc-in和Haskell的镜头有什么区别?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆