使用状态单子中的镜头访问数组元素

3

如果值类型不是单子,使用 lens 在状态单子内访问数组元素的推荐方式是什么?

以下代码将无法编译,因为 lens 不知道如何处理给定索引 i 处没有元素的情况。

type MyArray = Array Int Char
-- accessElemInStateWrong :: Int -> State MyArray Char
-- accessElemInStateWrong i = use $ ix i

通过将Control.Monad.State.Class中的getsControl.Lens.Fold中的preview结合使用,可以实现一个工作版本。

accessElemInState :: Int -> State MyArray (Maybe Char)
accessElemInState i = gets $ preview $ ix i

这很好运作。然而,鉴于lens定义的丰富函数和运算符,我惊讶地发现似乎没有特别针对此情况的函数。

因此,我的问题是:lens是否定义了类似于gets . preview的函数?如果没有,实现accessElementInState的推荐方法是什么?


我提出这个问题的原因是因为lens在状态单子之外定义了一个特殊的运算符。虽然以下代码由于相同的原因无法编译。

-- accessElemWrong :: Int -> MyArray -> Char
-- accessElemWrong i a = a ^. ix i

我们可以使用操作符(^?)将结果包装在Maybe中并进行安全查找。
accessElem :: Int -> MyArray -> Maybe Char
accessElem i a = a ^? ix i

我很好奇:为什么 use $ ix i 不起作用?错误信息是什么? - Joachim Breitner
1
@JoachimBreitner 错误信息是 Char 没有 Monoid 实例。如果元素类型是一个 Monoid 并且我们试图访问一个缺失的元素,那么 lens 将返回 mempty。然而,由于 Char 没有 Monoid 实例,因此 lens 不知道在这种情况下该怎么做。 - Lemming
1个回答

2

有一个名为preuse的函数,听起来正是您需要的:

accessElemInState :: Int -> State MyArray (Maybe Char)
accessElemInState i = preuse $ ix i

-- or
accessElemInState = preuse . ix

谢谢,这确实是我正在寻找的。你有什么提示可以帮助我找到类似的东西吗?我觉得镜头的文档非常难以使用,并不知道在hoogle上搜索什么类型。 - Lemming
@Lemming 对我来说,Hoogle 搜索 lens 真的很难。但是如果你在 repl 中执行类似 :t gets . preview 的操作并插入,通常可以得到结果。总的来说,pre 可以让你将接受镜头(如 useview)的函数转换为接受棱镜的函数。例如,preview = view . prepreuse = use . pre 等等。如果你学会了这门语言——文字游戏——你就能更好地预测出其中的内容。(我还不太擅长这个。) - Ian Henry
谢谢提示。事实证明,这在www.haskell.org/hoogle上没有产生任何结果。然而,由于有了nix,我可以很容易地制作自己的hoogle:nix-shell -p 'haskell.packages.ghc7102.ghcWithHoogle (pkgs: with pkgs; [ lens ])',如果我在里面搜索Getting (First a) s a -> m (Maybe a),那么它会输出preuse作为第一个结果。 - Lemming
如果你添加了+lens,它似乎在vanilla Hoogle上能够正常工作。 - Ian Henry
我之前不知道那个功能。谢谢! - Lemming

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接