Haskell 中的镜头与 Clojure 中的解构有何异同?
What are the similarities and differences between a lens in Haskell and destructuring in Clojure?
假设:
- 我想解析嵌套的 JSON 字符串。
- 我知道 Haskell 鼓励使用类型系统解决问题,而 Clojure 避开类型系统,更喜欢使用数据结构解决问题。
- 我知道在两种语言中这个过程都被称为 destructuring - 但它在 Clojure 中没有其他名称,而在 Haskell 中这个也被称为 using a lens,所以我称 Clojure 为 destructuring,然后继续
我们可以看到可以在Haskell中创建一个lens like this:
{-# LANGUAGE TemplateHaskell #-}
import Control.Lens
initialState :: Game
initialState = Game
{ _score = 0
, _units =
[ Unit
{ _health = 10
, _position = Point { _x = 3.5, _y = 7.0 }
}
]
}
health :: Lens' Unit Int
health = lens _health (\unit v -> unit { _health = v })
它的目的是从 game
数据结构中获取 health
值。
我们可以在 Clojure 中进行解构 like this:
user=> (def book {:name "SICP" :details {:pages 657 :isbn-10 "0262011530"}})
#'user/book
user=> (let [{name :name {pages :pages isbn-10 :isbn-10} :details} book]
(println "name:" name "pages:" pages "isbn-10:" isbn-10))
name: SICP pages: 657 isbn-10: 0262011530
目的是从嵌套结构中获取名称、页面等的嵌套值。
在这两种情况下,您都想出了一种机制来从嵌套结构中可重用地检索值。
我的问题是:Haskell 中的镜头与 Clojure 中的解构有何异同?
您的 Clojure 代码最接近于在 Haskell 中这样表达:
book :: Book
book = Book {name = "SICP", details = Details {pages = 657, isbn_10: "0262011530"}}
main = do
let Book {name = name, details = Details {pages = pages, isbn_10 = isbn_10}} = book
putStrLn $ "name:" ++ name ++ "pages:" ++ (show pages) ++ "isbn-10:" ++ isbn-10
这里我们使用了 Haskell 的模式匹配,它基本上是 Clojure 解构绑定的一种更通用的形式,以获取记录的内容。这与镜头无关。
现在让我们用镜头看一下您的 Haskell 代码。此代码具有三种记录类型,Game、Unit 和 Point,并且这些类型各自具有一组自动定义的函数来访问其成员而无需模式匹配。这些函数称为 _score
、_health
、_x
等(就像成员名称一样)。它们分别采用 Game
、Unit
或 Point
并产生各自成员的值。如果必须手动定义这些函数(它们不需要,因为 Haskell 会自动为所有记录类型定义此类函数),它看起来像这样:
_health (Unit {health = h}) = h
到目前为止,这仍然与镜头无关。
代码中的镜头是health
。它包含函数 _health
(如前所述,它接受一个 Unit
并产生它的生命值)和函数 (\unit v -> unit { _health = v })
,它接受一个单位和一个值并产生一个新单位,其生命值已设置为该值。换句话说,镜头包含获取和设置单元健康状况的代码。镜头库现在包含各种函数,您可以使用这些函数来组合和使用此类镜头。
所以回答你的问题:镜头和解构之间没有相似之处。
假设:
- 我想解析嵌套的 JSON 字符串。
- 我知道 Haskell 鼓励使用类型系统解决问题,而 Clojure 避开类型系统,更喜欢使用数据结构解决问题。
- 我知道在两种语言中这个过程都被称为 destructuring - 但它在 Clojure 中没有其他名称,而在 Haskell 中这个也被称为 using a lens,所以我称 Clojure 为 destructuring,然后继续
我们可以看到可以在Haskell中创建一个lens like this:
{-# LANGUAGE TemplateHaskell #-}
import Control.Lens
initialState :: Game
initialState = Game
{ _score = 0
, _units =
[ Unit
{ _health = 10
, _position = Point { _x = 3.5, _y = 7.0 }
}
]
}
health :: Lens' Unit Int
health = lens _health (\unit v -> unit { _health = v })
它的目的是从 game
数据结构中获取 health
值。
我们可以在 Clojure 中进行解构 like this:
user=> (def book {:name "SICP" :details {:pages 657 :isbn-10 "0262011530"}})
#'user/book
user=> (let [{name :name {pages :pages isbn-10 :isbn-10} :details} book]
(println "name:" name "pages:" pages "isbn-10:" isbn-10))
name: SICP pages: 657 isbn-10: 0262011530
目的是从嵌套结构中获取名称、页面等的嵌套值。
在这两种情况下,您都想出了一种机制来从嵌套结构中可重用地检索值。
我的问题是:Haskell 中的镜头与 Clojure 中的解构有何异同?
您的 Clojure 代码最接近于在 Haskell 中这样表达:
book :: Book
book = Book {name = "SICP", details = Details {pages = 657, isbn_10: "0262011530"}}
main = do
let Book {name = name, details = Details {pages = pages, isbn_10 = isbn_10}} = book
putStrLn $ "name:" ++ name ++ "pages:" ++ (show pages) ++ "isbn-10:" ++ isbn-10
这里我们使用了 Haskell 的模式匹配,它基本上是 Clojure 解构绑定的一种更通用的形式,以获取记录的内容。这与镜头无关。
现在让我们用镜头看一下您的 Haskell 代码。此代码具有三种记录类型,Game、Unit 和 Point,并且这些类型各自具有一组自动定义的函数来访问其成员而无需模式匹配。这些函数称为 _score
、_health
、_x
等(就像成员名称一样)。它们分别采用 Game
、Unit
或 Point
并产生各自成员的值。如果必须手动定义这些函数(它们不需要,因为 Haskell 会自动为所有记录类型定义此类函数),它看起来像这样:
_health (Unit {health = h}) = h
到目前为止,这仍然与镜头无关。
代码中的镜头是health
。它包含函数 _health
(如前所述,它接受一个 Unit
并产生它的生命值)和函数 (\unit v -> unit { _health = v })
,它接受一个单位和一个值并产生一个新单位,其生命值已设置为该值。换句话说,镜头包含获取和设置单元健康状况的代码。镜头库现在包含各种函数,您可以使用这些函数来组合和使用此类镜头。
所以回答你的问题:镜头和解构之间没有相似之处。