哈斯克尔使用第一级镜头来创建复杂的镜头 [英] Haskell use first level lenses to create complex lens

查看:104
本文介绍了哈斯克尔使用第一级镜头来创建复杂的镜头的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

  data示例=示例{_position :: Int 
,_storage :: [Int]}

我如何构建一个专注于 position 元素在存储中



另外,是否有可能通过镜片将位置值修改为基于存储大小的范围



好像除了可以以某种方式使用,因为示例同构于一个元组,但我无法理解这种方式。



我不确定如何解释这个问题,所以我找不到相关的信息。

解决方案

我不知道ny combinator可以做你想做的,所以我写了一个。

 (^>> =):: Lens'sa - > (a-> Lens's b) - > Lens的b 
- Lens的a - > (a - >(b→f b)→s→f s)→> (b→f b)→> s - > fs
- (前一行忽略了全部和Functor约束)
(x ^> gt; = f)btofb s = f(s ^ x)btofb s

退出类型签名并询问ghci应该给我们一个最通用的签名,所以这里是:

 :t(^>> =)
获取asa - > (a→t1→s→t)→> t1 - > s - > t

获取文档:当您在类型签名中看到它时,表示您可以传递该函数一个镜头,吸气剂,遍历,折叠,棱镜,Iso或其中一个索引变体,它只会做正确的事情。

右侧是类似的一般,允许遍历/棱镜/等。



请注意,这只会产生合法的lenslikes,如果指针不是它本身。



现在应用这个组合器 - 你想要的组合是:

pre $ position ^>> = \p - >存储。 ix p

这是一个遍历,请参阅原始答案。
$ b

或者,使用另一个combinator我喜欢:

  let(f。:g)x = F 。 gx in position ^>> =(storage。:ix)



(原始答案假定 position :: Int 局部阴影位置镜头。)



我们不知道该列表是否在该位置有一个值,所以这不是Lens',而是一个遍历,代表遍历任何数量的值,而不是透视到一个值。

  storage 。 ix position :: Traversal'示例Int 

(^?)将返回遍历的第一个值,因此如果该位置有效,则该术语将给予Int,否则为无。

 (^ ?storage。ix position)::示例 - >也许Int 

这个部分版本会假设位置是有效的,如果不是。

 (^ ?! storage。ix position)::示例 - > Int 

(%〜),它将右侧的函数应用于遍历左侧的所有内容,不仅适用于镜头,而且适用于所有遍历。 (每个镜头都是巧妙的ekmett技巧的遍历,并且可以插入遍历的任何地方。)

 存储。 ix位置%〜(+1)::示例 - >示例

如果您绝对必须使用Lens,那么如果尝试使用这些部分字词将它们应用于无效位置。

 单数$存储。 ix position :: Lens'示例Int 
存储。 PS:你的记录看起来像你可能想要的拉链相反:如果你只是向前/向后递增,如果你跟踪当前位置左侧的值列表,当前位置的值和列表中的值,那么你会少做臭(!!)的东西的价值在您当前位置的右侧,而不是所有值的列表以及您在其中的位置。要获得更多的镜头乐趣,请查看 Control.Lens.Zipper ,但这些优化可以优美地嵌套多个级别的拉链。


Let's say, I have an object with two fields:

data Example = Example { _position :: Int
                       , _storage  :: [Int]}

how do I construct a lens that focuses on position element inside storage?

Also, will it be possible to restrict position values being modified via lenses to a range based on storage size?

It seems like alongside could be used somehow, since Example is isomorphic to a tuple, but I can't comprehend the way to do that.

I'm not sure how to phrase the question, so I was unable to find much relevant info.

解决方案

Edit: I misunderstood the problem, original answer follows at the end.

I don't know any combinator that does what you want, so I wrote one.

(^>>=) :: Lens' s a -> (a -> Lens' s b) -> Lens' s b
--        Lens' s a -> (a -> (b -> f b) -> s -> f s) -> (b -> f b) -> s -> f s
-- (That previous line disregards a forall and the Functor constraints)
(x ^>>= f) btofb s = f (s ^. x) btofb s

Leaving out the type signature and asking ghci for it should give us the most general one, so here goes:

:t (^>>=)
Getting a s a -> (a -> t1 -> s -> t) -> t1 -> s -> t

Getting's doc: "When you see this in a type signature it indicates that you can pass the function a Lens, Getter, Traversal, Fold, Prism, Iso, or one of the indexed variants, and it will just "do the right thing"."

The right side is similarly general, allowing Traversals/Prisms/etc..

Note that this only produces lawful lenslikes if the pointer is not to itself.

Now to apply this combinator - the composition you wanted is:

position ^>>= \p -> storage . ix p

This comes out to be a Traversal, see the original answer.

Or, using another combinator I like:

let (f .: g) x = f . g x in position ^>>= (storage .: ix)

Any with some infix declarations you could even get rid of those brackets.


(This original answer assumes position :: Int locally shadows the position lens.)

We do not know whether the list has a value at that position, so this is not a Lens', but a Traversal', which stands for "traversing over any number of values" rather than "lensing onto one value".

storage . ix position :: Traversal' Example Int

(^?) will return the first value traversed over if any, and thus this term will give you the Int if that position is valid, or Nothing if it isn't.

(^? storage . ix position) :: Example -> Maybe Int

This partial version of that will assume that the position is valid and crash if it isn't.

(^?! storage . ix position) :: Example -> Int

(%~), which applies the function on the right to everything traversed over on the left, works not only for Lenses but all Traversals. (Every Lens is a Traversal by clever ekmett-trickery, and can be inserted anywhere a Traversal can go.)

storage . ix position %~ (+1) :: Example -> Example

And if you absolutely must work with a Lens, any of these partial terms will crash if you try to apply them at invalid positions.

singular $ storage . ix position :: Lens' Example Int
storage . singular (ix position) :: Lens' Example Int

PS: Your record looks like you might want zippers instead: If you only ever move forward/backward incrementally, you'd do less smelly (!!) stuff if you keep track of the list of values to the left of your current position, the value at your current position, and the list of values to the right of your current position, rather than the list of all values and your position in it. For more lensy fun, check out Control.Lens.Zipper, but those are optimized to gracefully nest multiple levels of zippering.

这篇关于哈斯克尔使用第一级镜头来创建复杂的镜头的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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