简体   繁体   English

ReifiedTraversal 中隐藏的所有量化类型

[英]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.感觉就像一个黑客,或者更确切地说,原始错误是类型检查器的疏忽。

The "adding" of traversals that you want was added in the most recent lens release, you can find it under the name adjoin .您想要的遍历的“添加”已在最新的镜头版本中添加,您可以在名称adjoin下找到它。 Note that it is unsound to use if your traversals overlap at all.请注意,如果您的遍历完全重叠,则使用它是不合理的。

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仍然是LensLikeASetter ,因此您可以使用镜头包中的许多运算符,例如.~

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM