这个问题确实更笼统,因为当我问它时,我发现了如何在这种特殊情况下解决它(即使我不喜欢它),但我会在我的特定上下文中使用它。
语境:
我正在使用镜头库,我发现为“添加”遍历提供功能特别有用(从概念上讲,遍历两个原始遍历中的所有元素的遍历)。我没有找到默认实现,所以我使用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...
我知道这与定义forall f. Functor f =>
中的隐藏有关Traversal
。在写这篇文章时,我意识到以下确实有效:
r_lens_idx :: Int -> ReifiedTraversal' [a] a
r_lens_idx idx = Traversal (lens_idx idx)
因此,通过给它参数,它可以使自己f
显式,然后它可以使用它。但是,这感觉非常临时。特别是因为最初我试图在函数r_lens_idx
定义的 where 子句中构建这个内联traversal_idxs
(实际上......在定义这个内联函数的函数上,因为我真的不会经常使用它)。
所以,当然,我想我总是可以使用 lambda 抽象,但是......这真的是处理这个问题的正确方法吗?感觉就像是 hack,或者更确切地说,原始错误是类型检查器的疏忽。