Скрытые все количественные типы в ReifiedTraversal - PullRequest
2 голосов
/ 13 января 2020

Этот вопрос действительно более общий 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, потому что я не собираюсь использовать ее часто).

Так что, конечно, я думаю, я всегда могу использовать лямбда-абстракцию, но ... действительно ли это правильный способ справиться с этим? Это похоже на хак, или, скорее, на то, что первоначальная ошибка - это упущение проверки типов.

Ответы [ 2 ]

1 голос
/ 12 февраля 2020

«Добавление» нужных вам обходов было добавлено в самой последней версии объектива, вы можете найти его под именем adjoin . Обратите внимание, что это неправильно использовать, если ваши обходы вообще перекрываются.

0 голосов
/ 14 января 2020

Я отвечаю на свой вопрос, хотя он только указывает на то, что то, что я пытался сделать с обходами, на самом деле было невозможно в этой форме, и как я преодолел это. Все еще существует основная проблема скрытых глобальных переменных и как возможно, что лямбда-абстракция может сделать код, который не проверяет тип, внезапно проверяет тип (или, скорее, почему он не проверял тип, чтобы начать с ).

Оказывается, моя реализация Monoid для Traversal была глубоко испорчена. Я понял, когда начал отлаживать его. Например, я пытался объединить список индексов и функцию, которая возвращала бы линзу для каждого индекса, отображая этот индекс в списке, в обход, который соответствовал бы именно этим индексам. Это возможно, но опирается на тот факт, что List является Monad, а не просто использует структуру 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 по-прежнему LensLike и ASetter, так что вы можете используйте множество операторов из пакета объективов, например .~.

. Как я уже упоминал, я все еще должен использовать лямбда-абстракцию, когда использую это в своих целях, поскольку квантификатор forall находится в неудобном месте, и Я был бы рад, если бы кто-то мог уточнить, что, черт возьми, происходит с проверкой типов в этом отношении.

...