【发布时间】:2019-03-15 19:49:31
【问题描述】:
简述
我的 getter 和 setter 都可能失败,并带有描述如何的消息。因此他们返回Either String,这意味着我不能以正常方式用它们制作镜片。
详细说明
考虑这些类型:
import qualified Data.Vector as V
data Tree a = Tree { label :: a
, children :: V.Vector (Tree a) }
type Path = [Int]
并非每个Path 到Tree 都会导致Tree,因此getter 必须具有类似getSubtree :: Path -> Tree a -> Either String (Tree a) 的签名。一个 setter 需要一个类似的签名(见下面的modSubtree)。
如果 getter 和 setter 返回 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
【问题讨论】:
-
一目了然,看起来应该可以有the
ixtraversal之类的东西。您会丢失错误消息,但这可能不是一个很大的成本(据我所知,它们实际上并没有带来有关失败的额外信息)。 -
@duplode,这太值得了!现在我首先使用一个函数来测试路径是否在边界内,然后使用 ix.我将保留这个问题,希望有一天有人能解决一般问题——因为在某些情况下,Left 中的数据很重要——但在我的具体情况下,你的解决方案是完美的。
标签: haskell haskell-lens either