阵列索引镜头阵列和索引镜头

时间:2014-12-02 19:11:40

标签: haskell composition lens lenses

这是Using lens for array indexing if both array and index are in State的更简单版本,因为我已经解决了一些问题。我不确定是否应删除原件或将其编辑到位。

鉴于

{-# 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镜头。

我的问题是:combo1arr中构建idx的最惯用的方式是什么?

2 个答案:

答案 0 :(得分: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

答案 1 :(得分:1)

事实证明,由于!是部分的,Lens'不是可行的方式,我应该使用Traversal'代替:

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

comboGetcomboSet可以使用镜头组合器重写:

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)
@chaosmasttter的

combo可以写成:

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中指出的那样,关键是要扩大镜头构成以便访问m