当我的getter和setter返回“ Either”时,我可以制作像镜头一样的东西吗?

Jef*_*own 5 haskell either haskell-lens

简单来说

我的getter和setter都可能失败,并带有描述如何操作的消息。因此它们会返回Either String,这意味着我无法以正常方式用它们制作镜头。

详细

考虑以下类型:

import qualified Data.Vector as V

data Tree a = Tree { label :: a
                   , children :: V.Vector (Tree a) }

type Path = [Int]
Run Code Online (Sandbox Code Playgroud)

并非每Path一个Tree线索都导致一个Tree,因此吸气剂必须具有类似的签名getSubtree :: Path -> Tree a -> Either String (Tree a)。设置者需要类似的签名(请参见modSubtree下文)。

如果getter和setter返回type的值Tree a,我将使用它们通过Lens.Micro中lens函数来创建镜头。但是,如果他们回来了,我就不能这样做。因此,我无法将它们与其他镜头组合在一起,因此必须进行很多包装和拆包。Either

有什么更好的方法?

范例程式码

{-# LANGUAGE ScopedTypeVariables #-}

module I_wish_I_could_lens_this_Either where

import qualified Data.Vector as V

data Tree a = Tree { label :: a
                   , children :: V.Vector (Tree a) }
              deriving (Show, Eq, Ord)

type Path = [Int]

-- | This is too complicated.
modSubtree :: forall a. Show a =>
  Path -> (Tree a -> Tree a) -> Tree a -> Either String (Tree a)
modSubtree [] f t = Right $ f t
modSubtree (link:path) f t = do
  if not $ inBounds (children t) link
    then Left $ show link ++ "is out of bounds in " ++ show t
    else Right ()
  let (cs :: V.Vector (Tree a)) = children t
      (c :: Tree a) = cs V.! link
  c' <- modSubtree path f c
  cs' <- let left = Left "imossible -- link inBounds already checked"
         in maybe left Right $ modifyVectorAt link (const c') cs
  Right $ t {children = cs'}

getSubtree :: Show a => Path -> Tree a -> Either String (Tree a)
getSubtree [] t = Right t
getSubtree (link:path) t =
  if not $ inBounds (children t) link
  then Left $ show link ++ "is out of bounds in " ++ show t
  else getSubtree path $ children t V.! link

-- | check that an index into a vector is inbounds
inBounds :: V.Vector a -> Int -> Bool
inBounds v i = i >= 0 &&
               i <= V.length v - 1

-- | Change the value at an index in a vector.
-- (Data.Vector.Mutable offers a better way.)
modifyVectorAt :: Int -> (a -> a) -> V.Vector a -> Maybe (V.Vector a)
modifyVectorAt i f v
  | not $ inBounds v i = Nothing
  | otherwise = Just ( before
                       V.++ V.singleton (f $ v V.! i)
                       V.++ after )
    where before = V.take i v
          after = V.reverse $ V.take remaining $ V.reverse v
            where remaining = (V.length v - 1) - i
Run Code Online (Sandbox Code Playgroud)