我认为你可能无法获得完全符合你要求的内容,但你可以获得一些相关的东西。这个答案会采用一个比较迂回的方式来达到我认为你最有可能想要的东西;这是我的思维路径接近结论所采取的方式,我认为这证明了我最终得出的结论的合理性。总的主题是,你可以应用几种不同的合法视角来看待你的情况,并且在不同方面都能够有所帮助。
首先,让我们看看你可以获得哪种类型的“镜头”。i 和 l Nat 标记出了向量 n 中的一个“窗口”。你没有说明如果窗口不完全位于向量中,你希望发生什么。其中一种选择是要求它适合。另一种选择是将窗口剪裁到向量的大小:
type Min m n = Min' (m <=? n) m n
type family Min' m_le_n (m :: Nat) (n :: Nat) where
Min' 'True m _ = m
Min' 'False _ n = n
type SatMinus m n = SatMinus' (n <=? m) m n
type family SatMinus' n_le_m m n where
SatMinus' 'True m n = m - n
SatMinus' 'False _ _ = 0
type ClippedLength i l n = Min l (SatMinus n i)
现在你可以为每个n(使用类来定义,本文的其余部分将忽略此细节)定义一个合法的。
vlCut :: (KnownNat i, KnownNat l)
=> Proxy i -> Proxy l
-> Lens' (Vector n a) (Vector (ClippedLength i l n) a)
或者,如果您只想允许适合的窗口,
vl :: (KnownNat i, KnownNat j, i + l <= n)
=> Proxy i -> Proxy l
-> Lens' (Vector n a) (Vector l a)
我们现在可以通过其中一个透镜进行工作,而不失去任何一般性(尽管我们会失去效率;稍后再详细介绍如何解决这个问题)。这样做意味着我们完全可以忽略窗口外的所有内容,因此我们不需要再提到代理。如果我们有从
Vector w a
到
t
的光学器,则我们可以产生从
Vector n a
到
t
的光学器。
将您的切片操作函数缩小到窗口,您将获得
getSliceW :: Vector w a -> [a]
setSliceWpartial :: Vector w a -> [a] -> Maybe (Vector w a)
这些并不能组成一个
Lens
,正如你所发现的。但是如果你再进一步简化,将
setSliceWpartial
替换为...
fromList :: [a] -> Maybe (Vector w a)
你可以制作一个合法的
Prism
:
slicep :: Prism' [a] (Vector w a)
给定一个类型为
Vector w a
的值,你总是可以生成一个类型为
[a]
的值,但反过来不一定成立。你当然可以在使用
vl
或
vlCut
时使用它(如果这是你需要解决的问题,那么这是一个好的解决方案),但是你不能与它们
组合,因为类型不匹配。你可以使用
re
翻转棱镜,但最终只会得到一个
Getter
。
由于您的类型似乎无法很好地工作,让我们尝试更改它们:
getSliceW :: Vector w a -> [a]
setSliceW :: Vector w a -> [a] -> Vector w a
现在我们开始使用 bass 进行编程!这个东西的类型类似于一个 Lens' (Vector w a) [a],虽然它实际上不是一个合法的 lens。但是,它是一个非常好的线索。Control.Lens.Traversal 提供了相关内容。
partsOf' :: ATraversal s t a a -> Lens s t [a] [a]
“在这个上下文中,你可以将其解读为”
partsOf' :: Traversal' (Vector w a) a -> Lens' (Vector w a) [a]
所以(透过窗户),我们
真正想要的是:
traverseVMono :: Traversal' (Vector w a) a
当然,这可以立即概括;只需为
Vector n
编写一个
Traversable
实例并使用其
traverse
即可。
我之前提到,通过窗口Lens
工作效率低下。那么你该如何处理呢?好吧,其实不需要真正构建窗口!你想要的是“从头到尾”地遍历窗口。所以就这样做:
traverseWindow :: (KnownNat i, KnownNat l, Applicative f)
-- optionally require i + l <= n
=> proxy1 i
-> proxy2 l
-> (a -> f a)
-> Vector n a
-> f (Vector n a)
如果您愿意,您可以恢复原始的部分
setSlice
;您只需要使用类似于
MaybeT(State [a])
的内容来使用
traverseWindow
:
foldMapWindow :: (KnownNat i, KnownNat l, Monoid m)
=> proxy1 i
-> proxy2 l
-> (a -> m)
-> Vector n a
-> m
foldMapWindow p1 p2 f = getConst . traverseWindow p1 p2 (Const . f)
windowToList :: (KnownNat i, KnownNat l)
=> proxy1 i
-> proxy2 l
-> Vector n a
-> [a]
windowToList p1 p2 = foldMapWindow p1 p2 (:[])
setSlice :: (KnownNat i, KnownNat l)
=> proxy1 i -> proxy2 l
-> Vector n a -> [a] -> Maybe (Vector n a)
setSlice p1 p2 v xs = flip evalState (windowToList p1 p2 v) . runMaybeT $ flip (traverseWindow p1 p2) v $ \_ -> do
y : ys <- get
put ys
pure y
getSliceW :: Vector w a -> [a]
和setSliceWpartial :: Vector w a -> [a] -> Maybe (Vector w a)
构建slicep :: Prism' [a] (Vector w a)
。它们无法适配于prism' :: (b -> s) -> (s -> Maybe a) -> Prism s s a b
:setSlice
有一个额外的参数。 - Alexey VagarenkosetSliceWPartial
,而是一个更为有限的fromList :: [a] -> Maybe (Vector w a)
。 - dfeuer