Этот вопрос действительно более общий c, так как, когда я его задавал, я выяснил, как это исправить в данном конкретном случае (даже если мне это не нравится), но я сформулирую его в моем конкретном контексте .
Контекст:
Я использую библиотеку линз, и мне было особенно полезно обеспечить функциональность для "добавления" обходов (концептуально - обхода, который пересекает все элементы в обоих исходных обходах). Я не нашел реализацию по умолчанию, поэтому я сделал это, используя 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
явным для себя, а затем он может работать с ним. Тем не менее, это чувствует себя очень ad-ho c. Специально потому, что изначально я пытался встроить этот r_lens_idx
inline в предложение where в определении функции traversal_idxs
(фактически ... в функции, определяющей эту функцию inline, потому что я не собираюсь использовать ее часто).
Так что, конечно, я думаю, я всегда могу использовать лямбда-абстракцию, но ... действительно ли это правильный способ справиться с этим? Это похоже на хак, или, скорее, на то, что первоначальная ошибка - это упущение проверки типов.