0

如果数组和索引都处于状态,这是使用镜头进行数组索引的更简单版本,因为我已经解决了一些问题。我不确定是否应该删除原件或原地编辑。

给定

{-# Language TemplateHaskell #-}
{-# Language Rank2Types #-}

import Control.Lens
import Control.Lens.TH
import Data.Array

data M = M { _arr :: Array Int Int, _idx :: Int } deriving (Show)

$(makeLenses ''M)

我想写一个函数

combine :: Lens' M (Array Int Int) -> Lens' M Int -> Lens' M Int

这需要arridx镜头并构造一个组合镜头,可用于读取和写入 指向的元素idx。我想要的镜头存在:

comboGet :: M -> Int
comboGet m = _arr m ! _idx m

comboSet :: M -> Int -> M
comboSet m v = m { _arr = _arr m // [(_idx m, v)] }

combo1 :: Simple Lens M Int
combo1 = lens comboGet comboSet

我理解这一点comboGetcomboSet原则上可以重写为单独使用arridx镜头。

combo1我的问题是:用arrand构建最惯用的方法是什么idx

4

2 回答 2

3

你可以这样构造它:

combo :: Lens' M Int -- alias for 'Functor f => (Int -> f Int) -> (M -> f M)
combo f m = arr (\xs -> fmap (\r -> xs // [(i, r)]) $ f $ xs ! i) m
  where i = m^.idx

编辑

更好的Traversal版本如下所示:

betterCombo :: Traversal' M Int
betterCombo f m = arr (\xs -> maybe (pure xs) 
                                    ((\r -> xs // [(i, r)]) <$> f)
                                    xs^? ix i) m
  where i = m^.idx

这相当于:

betterCombo f m = (arr . ix i) f m
  where i = m^.idx
于 2014-12-02T20:11:41.233 回答
1

事实证明, as!是部分的,Lens'不是要走的路,我应该Traversal'改用:

combine :: Lens' M (Array Int Int) -> Lens' M Int -> Traversal' M Int

comboGet并且comboSet可以使用镜头组合器重写:

comboGet' :: M -> Int
comboGet' m = m ^?! almostCombo m

comboSet' :: M -> Int -> M
comboSet' m v = m & almostCombo m .~ v where

almostCombo :: M -> Traversal' M Int
almostCombo m = arr . ix (m ^. idx)

combo@chaosmasttter 的可以写成:

combo2 :: Functor f => (Int -> f Int) -> M -> f M
combo2 f m = arr (\xs -> fmap (\r -> (ix i .~ r) xs) . f $ xs ^?! ix i) m  
    where i = m^.idx

它的Traversal签名只会不同Applicative

combo :: Applicative f => (Int -> f Int) -> M -> f M

Applicative帮助我们使 getter 的结果成为可选的吗?

正如@chaosmasttter 在他的文章中指出的那样betterCombo,关键是 eta-expand 镜头构图以访问m.

于 2014-12-02T20:43:50.780 回答