【发布时间】: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] -> 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 => 有关。在写这篇文章时,我意识到以下方法确实有效:
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