我在OCaml中编码了一种van Laarhoven镜头,但由于价值限制我很难.
相关代码如下
module Optic : sig
type (-'s, +'t, +'a, -'b) t
val lens : ('s -> 'a) -> ('s -> 'b -> 't) -> ('s, 't, 'a, 'b) t
val _1 : ('a * 'x, 'b * 'x, 'a, 'b) t
end = struct
type (-'s, +'t, +'a, -'b) t =
{ op : 'r . ('a -> ('b -> 'r) -> 'r) -> ('s -> ('t -> 'r) -> 'r) }
let lens …Run Code Online (Sandbox Code Playgroud) 我正在努力更深入地了解lens库,因此我会使用它提供的类型.我已经有了一些镜片经验,并且知道它们有多么强大和方便.所以我转向棱镜,我有点迷失了.似乎棱镜允许两件事:
第一点看起来很有用,但通常一个不需要来自实体的所有数据,并且^?使用普通镜头可以获得所讨论Nothing的字段不属于实体所代表的分支,就像使用棱镜一样.
第二点......我不知道,可能有用吗?
所以问题是:我可以用棱镜做些什么我不能用其他光学器件?
编辑:谢谢大家的优秀答案和进一步阅读的链接!我希望我能接受他们.
我所知道的是一个有效,另一个没有.
上下文:
我有一个数据结构F,其中包含Data.Map.Map k S另一个数据结构S.我的目标是建立一个Lens给定的F,k并将描述一个领域S.
困难在于密钥k可能不存在于地图中.这很好,该函数可以在Maybe中包装它的返回.但是,我无法通过使用Maybe传播镜头at.在阅读了很多Stack Overflow答案之后,我遇到了这个问题.
事实证明,更换at与ix解决我的问题类型,如果我还换成(^.)用(^?).
问题:
似乎at并且ix做同样的事情,至少在这方面Map.两者都拿一把钥匙并给那个钥匙的价值一个'镜头'.但是,ix似乎与函数组合运算符很好(.).两者有什么区别?
关闭主题咆哮:
我像下一个人一样喜欢中缀运算符,但Control.Lens包看起来有点过分了.对于具有某些英文名称和某个键的新用户而言,会降低学习曲线.由于Lens库中使用了大量的包装器类,如果您还不知道发生了什么,则特别难以深入了解类型签名.为了天堂,我的代码开始看起来像Perl.
这是在Haskell中使用拉链的一个例子:
data Tree a = Fork (Tree a) (Tree a) | Leaf a
data Cxt a = Top | L (Cxt a) (Tree a) | R (Tree a) (Cxt a)
type Loc a = (Tree a, Cxt a)
left :: Loc a -> Loc a
left (Fork l r, c) = (l, L c r)
right :: Loc a -> Loc a
right (Fork l r, c) = (r, R l c)
top :: Tree a -> Loc a
top …Run Code Online (Sandbox Code Playgroud) 编译好:
type List a = [a]
Run Code Online (Sandbox Code Playgroud)
但是当我引入类约束时,编译器要求RankNTypes包括:
type List2 a = Num a => [a]
Run Code Online (Sandbox Code Playgroud)
包含该扩展后,它编译得很好.为什么编译代码需要扩展名?
编辑:为什么我首先需要约束?
我检查这个镜头类型(type RefF a b = Functor f => (b -> f b) -> (a -> f a)从)这个职位,并发现它实际需要的RankNTypes,因为的Functor约束.
在没有镜头的haskell中,我可以做以下事情:
data Item = Item { quantity :: Double, price ::Double }
cost :: Item -> Double
cost = (*) <$> quantity <*> price
Run Code Online (Sandbox Code Playgroud)
如果我使用镜头,我该怎么做相同的?我能做的最好的是
cost = to $ (*) <$> (^. quantity) <*> (^. price)
Run Code Online (Sandbox Code Playgroud)
有没有更好的办法 ?(当然我想要一个getter或同等的)
鉴于类型
data Prisoner = P { _name :: String
, _rank :: Int
, _cereal :: Cereal }
data Cereal = C { _number :: Int
, _percentDailyValue :: Map String Float
, _mascot :: String }
Run Code Online (Sandbox Code Playgroud)
我可以通过模式匹配来提取某人的姓名,等级和谷物数量:
getNameRankAndCerealNumber_0 :: Prisoner -> (String, Int, Int)
getNameRankAndCerealNumber_0 (P { _name=name
, _rank=rank
, _cereal = C { _number=cerealNumber }}
) = (name, rank, cerealNumber)
Run Code Online (Sandbox Code Playgroud)
或者,我可以使用镜头分别提取每个部分
makeLenses ''Cereal
makeLenses ''Prisoner
getNameRankAndCerealNumber_1 :: Prisoner -> (String, Int, Int)
getNameRankAndCerealNumber_1 p = (p ^. …Run Code Online (Sandbox Code Playgroud) 我正在观看Control.Lens介绍视频.
这让我想知道为什么Setter类型需要在仿函数中包装它.
它(大致)定义如下:
type Control.Lens.Setter s t a b = (Functor f) => (a -> f a) -> s -> f t
Run Code Online (Sandbox Code Playgroud)
假设我有一个名为Pointthis的定义数据:
data Point = Point { _x :: Int, _y :: Int } deriving Show
Run Code Online (Sandbox Code Playgroud)
然后我可以xlens像这样写自己的:
type MySetter s t a b = (a -> b) -> s -> t
xlens :: MySetter Point Point Int Int
xlens f p = p { _x = f (_x p) …Run Code Online (Sandbox Code Playgroud) lensoffer holesOf,这是这个假设函数的一个更通用和更强大的版本:
holesList :: Traversable t
=> t a -> [(a, a -> t a)]
Run Code Online (Sandbox Code Playgroud)
给定一个容器,holesList生成一个容器元素列表以及替换这些元素的函数.
holesList类似于真实的类型holesOf,无法捕获生成的对数等于容器元素数的事实.因此,更美丽的类型将是
holes :: Traversable t
=> t a -> t (a, a -> t a)
Run Code Online (Sandbox Code Playgroud)
我们可以holes通过使用holesList制作一个列表然后遍历来State重新填充元素来实现.但这有两个原因令人不满意,其中一个原因具有实际后果:
slurping代码将有一个无法访问的错误调用来处理在遍历完成之前列表为空的情况.这很恶心,但对使用该功能的人来说可能并不重要.
无限延伸到左侧或左下方的容器根本不起作用.向左延伸很远的容器处理效率非常低.
我想知道是否有任何办法解决这些问题.使用Magma镜头之类的东西很有可能捕捉到遍历的形状:
data FT a r where
Pure :: r -> FT a r
Single :: a -> FT a a
Map :: (r -> s) -> FT a r -> FT …Run Code Online (Sandbox Code Playgroud) 使用Ramda.js(和镜头),我想修改下面的JavaScript对象,将具有ID ="/ 1/B/i"的对象的"NAME:VERSION1"更改为"NAME:VERSION2".
我想使用镜头,因为我只想更改一个深度嵌套的值,但保持整个结构不变.
我不想使用lensIndex,因为我永远不知道数组的顺序,所以相反,我想通过查找它的"id"字段来"找到"数组中的对象.
我可以用镜头做这个,还是应该用不同的方式做?
{
"id": "/1",
"groups": [
{
"id": "/1/A",
"apps": [
{
"id": "/1/A/i",
"more nested data skipped to simplify the example": {}
}
]
},
{
"id": "/1/B",
"apps": [
{ "id": "/1/B/n", "container": {} },
{
"id": "/1/B/i",
"container": {
"docker": {
"image": "NAME:VERSION1",
"otherStuff": {}
}
}
}
]
}
]
}
Run Code Online (Sandbox Code Playgroud) haskell-lens ×10
haskell ×8
applicative ×1
clojure ×1
functor ×1
javascript ×1
lenses ×1
ocaml ×1
ramda.js ×1
traversable ×1
zipper ×1