[英]Hidden forall quantified types in ReifiedTraversal
This question really is more generic, since while I was asking it I found out how to fix it in this particular case (even though I don't like it) but I'll phrase it in my particular context.这个问题确实更通用,因为当我问它时,我发现了在这种特殊情况下如何解决它(即使我不喜欢它),但我会在我的特定上下文中对其进行表述。
Context:语境:
I'm using the lens library and I found it particularly useful to provide functionality for "adding" traversals (conceptually, a traversal that traverses all the elements in both original traversals).我正在使用镜头库,我发现它为“添加”遍历(从概念上讲,遍历两个原始遍历中的所有元素的遍历)提供功能特别有用。 I did not find a default implementation so I did it using
Monoid
.我没有找到默认实现,所以我使用
Monoid
做到了。 In order to be able to implement an instance, I had to use the ReifiedTraversal
wrapper, which I assume is in the library precisely for this purpose:为了能够实现一个实例,我不得不使用
ReifiedTraversal
包装器,我认为它在库中正是为此目的:
-- Adding traversals
add_traversals :: Semigroup t => Traversal s t a b -> Traversal s t a b -> Traversal s t a b
add_traversals t1 t2 f s = liftA2 (<>) (t1 f s) (t2 f s)
instance Semigroup t => Semigroup (ReifiedTraversal s t a b) where
a1 <> a2 = Traversal (add_traversals (runTraversal a1) (runTraversal a2))
instance Semigroup s => Monoid (ReifiedTraversal' s a) where
mempty = Traversal (\_ -> pure . id)
The immediate application I want to extract from this is being able to provide a traversal for a specified set of indices in a list.我想从中提取的直接应用是能够为列表中的一组指定索引提供遍历。 Therefore, the underlying semigroup is
[]
and so is the underlying Traversable
.因此,底层半群是
[]
,底层Traversable
。 First, I implemented a lens for an individual index in a list:首先,我为列表中的单个索引实现了一个镜头:
lens_idx :: Int -> Lens' [a] a
lens_idx _ f [] = error "No such index in the list"
lens_idx 0 f (x:xs) = fmap (\rx -> rx:xs) (f x)
lens_idx n f (x:xs) = fmap (\rxs -> x:rxs) (lens_idx (n-1) f xs)
All that remains to be done is to combine these two things, ideally to implement a function traversal_idxs :: [Int] -> Traversal' [a] a
剩下要做的就是将这两件事结合起来,理想情况下实现一个函数
traversal_idxs :: [Int] -> Traversal' [a] a
Problem:问题:
I get type checking errors when I try to use this.当我尝试使用它时,我收到类型检查错误。 I know it has to do with the fact that
Traversal
is a type that includes a constrained forall
quantifier in its definition.我知道这与
Traversal
是一种在其定义中包含受约束的forall
量词的类型有关。 In order to be able to use the Monoid
instance, I need to first reify the lenses provided by lens_idx
(which are, of course, also traversals).为了能够使用
Monoid
实例,我需要首先lens_idx
提供的lens_idx
(当然,这也是遍历)。 I try to do this by doing:我尝试通过以下方式做到这一点:
r_lens_idx :: Int -> ReifiedTraversal' [a] a
r_lens_idx = Traversal . lens_idx
But this fails with two errors (two versions of the same error really):但这失败并出现两个错误(实际上是同一错误的两个版本):
Couldn't match type ‘f’ with ‘f0’...
Ambiguous type variable ‘f0’ arising from a use of ‘lens_idx’
prevents the constraint ‘(Functor f0)’ from being solved...
I understand this has to do with the hidden forall f. Functor f =>
我知道这与隐藏的
forall f. Functor f =>
forall f. Functor f =>
in the Traversal
definition. Traversal
定义中的forall f. Functor f =>
。 While writing this, I realized that the following does work:在写这篇文章时,我意识到以下确实有效:
r_lens_idx :: Int -> ReifiedTraversal' [a] a
r_lens_idx idx = Traversal (lens_idx idx)
So, by giving it the parameter it can make the f
explicit to itself and then it can work with it.所以,通过给它参数,它可以使
f
对它自己显式,然后它可以使用它。 However, this feels extremely ad-hoc.然而,这感觉非常临时。 Specially because originally I was trying to build this
r_lens_idx
inline in a where clause in the definition of the traversal_idxs
function (in fact... on a function defining this function inline because I'm not really going to use it that often).特别是因为最初我试图在
traversal_idxs
函数定义的 where 子句中内联构建这个r_lens_idx
(实际上......在定义这个函数的函数内联,因为我真的不打算经常使用它)。
So, sure, I guess I can always use lambda abstraction, but... is this really the right way to deal with this?所以,当然,我想我总是可以使用 lambda 抽象,但是......这真的是处理这个问题的正确方法吗? It feels like a hack, or rather, that the original error is an oversight by the type-checker.
感觉就像一个黑客,或者更确切地说,原始错误是类型检查器的疏忽。
I am replying to my own question, although it is only pointing out that what I was trying to do with traversals was not actually possible in that shape and how I overcame it.我正在回答我自己的问题,尽管它只是指出我试图用遍历做的事情实际上在这种形状中是不可能的,以及我是如何克服它的。 There is still the underlying problem of the hidden forall quantified variables and how is it possible that lambda abstraction can make code that does not type check suddenly type check (or rather, why it did not type check to start with).
仍然存在隐藏的 forall 量化变量的潜在问题,以及 lambda 抽象怎么可能使不进行类型检查的代码突然进行类型检查(或者更确切地说,为什么它开始时没有进行类型检查)。
It turns out my implementation of Monoid
for Traversal
was deeply flawed.事实证明,我对
Monoid
for Traversal
实现存在严重缺陷。 I realized when I started debugging it.当我开始调试它时,我意识到了。 For instance, I was trying to combine a list of indices, and a function that would return a lens for each index, mapping to that index in a list, to a traversal that would map to exactly those indices.
例如,我试图将一个索引列表和一个函数组合起来,该函数将为每个索引返回一个镜头,映射到列表中的那个索引,再到一个可以精确映射到这些索引的遍历。 That is possible, but it relies on the fact that
List
is a Monad
, instead of just using the Applicative
structure.这是可能的,但它依赖于
List
是一个Monad
的事实,而不仅仅是使用Applicative
结构。
The function that I had written originally for add_traversal
used only the Applicative
structure, but instead of mapping to those indices in the list, it would duplicate the list for each index, concatenating them, each version of the list having applied its lens.我最初为
add_traversal
编写的函数只使用了Applicative
结构,但它不是映射到列表中的那些索引,而是复制每个索引的列表,将它们连接起来,列表的每个版本都应用了它的镜头。
When trying to fix it, I realized I needed to use bind
to implement what I really wanted, and then I stumbled upon this: https://www.reddit.com/r/haskell/comments/4tfao3/monadic_traversals/在尝试修复它时,我意识到我需要使用
bind
来实现我真正想要的,然后我偶然发现了这个: https : //www.reddit.com/r/haskell/comments/4tfao3/monadic_traversals/
So the answer was clear: I can do what I want, but it's not a Monoid
over Traversal
, but instead a Monoid
over MTraversal
.所以答案很明确:我可以做我想做的,但它不是
Monoid
over Traversal
,而是Monoid
over MTraversal
。 It still serves my purposes perfectly.它仍然完全符合我的目的。
This is the resulting code for that:这是由此产生的代码:
-- Monadic traversals: Traversals that only work with monads, but they allow other things that rely on the fact they only need to work with monads, like sum.
type MTraversal s t a b = forall m. Monad m => (a -> m b) -> s -> m t
type MTraversal' s a = MTraversal s s a a
newtype ReifiedMTraversal s t a b = MTraversal {runMTraversal :: MTraversal s t a b}
type ReifiedMTraversal' s a = ReifiedMTraversal s s a a
-- Adding mtraversals
add_mtraversals :: Semigroup t => MTraversal r t a b -> MTraversal s r a b -> MTraversal s t a b
add_mtraversals t1 t2 f s = (t2 f s) >>= (t1 f)
instance Semigroup s => Semigroup (ReifiedMTraversal' s a) where
a1 <> a2 = MTraversal (add_mtraversals (runMTraversal a1) (runMTraversal a2))
instance Semigroup s => Monoid (ReifiedMTraversal' s a) where
mempty = MTraversal (\_ -> return . id)
Note that MTraversal
is still a LensLike
and an ASetter
, so you can use many operators from the lens package, like .~
.请注意,
MTraversal
仍然是LensLike
和ASetter
,因此您可以使用镜头包中的许多运算符,例如.~
。
As I mentioned, though, I still have to use lambda abstraction when using this for my purposes due to the forall quantifier being in an uncomfortable place, and I'd love if someone could clarify what the heck is up with the type checker in that regard.不过,正如我所提到的,由于 forall 量词处于一个不舒服的地方,我仍然必须使用 lambda 抽象来实现我的目的,如果有人能澄清类型检查器到底是怎么回事,我很乐意看待。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.