【问题标题】:Hidden forall quantified types in ReifiedTraversalReifiedTraversal 中隐藏的所有量化类型
【发布时间】:2020-01-13 03:19:11
【问题描述】:

这个问题确实更笼统,因为当我问这个问题时,我发现了如何在这种特殊情况下解决它(即使我不喜欢它),但我会在我的特定上下文中使用它。

上下文:

我正在使用镜头库,我发现它为“添加”遍历(从概念上讲,一种遍历两个原始遍历中的所有元素的遍历)提供功能特别有用。我没有找到默认实现,所以我使用Monoid 完成了它。为了能够实现一个实例,我必须使用 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)

我想从中提取的直接应用程序是能够为列表中的指定索引集提供遍历。因此,底层半群是[],底层Traversable也是如此。首先,我为列表中的单个索引实现了一个镜头:

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)

剩下要做的就是把这两件事结合起来,理想情况下实现一个函数traversal_idxs :: [Int] -&gt; Traversal' [a] a

问题:

我在尝试使用它时遇到类型检查错误。我知道这与 Traversal 是一种在其定义中包含受约束的 forall 量词这一事实有关。为了能够使用Monoid 实例,我需要首先具体化lens_idx 提供的镜头(当然也是遍历)。我尝试这样做:

r_lens_idx :: Int -> ReifiedTraversal' [a] a
r_lens_idx = Traversal . lens_idx

但这会失败并出现两个错误(实际上是同一错误的两个版本):

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...

我知道这与 Traversal 定义中隐藏的 forall f. Functor f =&gt; 有关。在写这篇文章时,我意识到以下方法确实有效:

r_lens_idx :: Int -> ReifiedTraversal' [a] a
r_lens_idx idx = Traversal (lens_idx idx)

因此,通过给它一个参数,它可以使f 对自己显式,然后它可以使用它。但是,这感觉非常临时。特别是因为最初我试图在 traversal_idxs 函数定义中的 where 子句中构建这个 r_lens_idx 内联(实际上......在定义这个内联函数的函数上,因为我真的不会使用它经常)。

所以,当然,我想我总是可以使用 lambda 抽象,但是……这真的是处理这个问题的正确方法吗?感觉就像是 hack,或者更确切地说,原始错误是类型检查器的疏忽。

【问题讨论】:

  • 我认为ATraversal 更适合您的目的。它没有forall,因此可以避免您的问题。

标签: haskell haskell-lens forall


【解决方案1】:

您想要的遍历的“添加”是在最近的镜头版本中添加的,您可以在名称adjoin 下找到它。请注意,如果您的遍历完全重叠,则使用它是不合理的。

【讨论】:

    【解决方案2】:

    我正在回答我自己的问题,尽管它只是指出我试图用遍历做的事情在那种形状中实际上是不可能的,以及我是如何克服它的。 仍然存在隐藏的 forall 量化变量的潜在问题,以及 lambda 抽象如何可能使不进行类型检查的代码突然进行类型检查(或者更确切地说,为什么它没有开始类型检查与)。

    事实证明,我对TraversalMonoid 实现存在严重缺陷。当我开始调试它时,我意识到了。例如,我试图将一个索引列表和一个函数组合起来,该函数将为每个索引返回一个镜头,映射到列表中的那个索引,到一个将映射到这些索引的遍历。这是可能的,但它依赖于ListMonad这一事实,而不是仅仅使用Applicative 结构。

    我最初为add_traversal 编写的函数只使用了Applicative 结构,但它不会映射到列表中的那些索引,它会复制每个索引的列表,将它们连接起来,每个版本的列表已经应用了它的镜头。

    在尝试修复它时,我意识到我需要使用 bind 来实现我真正想要的,然后我偶然发现了这个:https://www.reddit.com/r/haskell/comments/4tfao3/monadic_traversals/

    所以答案很明确:我可以做我想做的事,但不是Monoid 之上的Traversal,而是Monoid 之上的MTraversal。它仍然完美地满足了我的目的。

    这是生成的代码:

    -- 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)
    

    请注意,MTraversal 仍然是 LensLikeASetter,因此您可以使用镜头包中的许多运算符,例如 .~

    不过,正如我所提到的,由于 forall 量词处于一个不舒服的位置,因此在将其用于我的目的时,我仍然必须使用 lambda 抽象,如果有人能澄清该类型到底是怎么回事,我会很高兴在这方面进行检查。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-05-30
      • 1970-01-01
      • 2023-03-27
      • 1970-01-01
      • 2013-03-30
      • 2020-12-10
      • 2018-05-23
      • 2016-08-14
      相关资源
      最近更新 更多