Подтвердить что ты не робот

Общий объектив, похожий на отображение и траверс

Предположим, я хотел создать "опцию" для содержимого MaybeT m a:

maybeTContents = _Wrapped . something . _Just

Есть ли такой something?

maybeTContents будет, например, быть Traversal, когда m есть [],  но только a Setter, когда m есть (->) Int.

Пример использования:

> MaybeT [Just 1, Nothing, Just 2] ^.. maybeTContents
[1, 2]
> runMaybeT (MaybeT (Just . ('e':)) & maybeTContents %~ ('H':)) "llo"
Just "Hello"
4b9b3361

Ответ 1

Один из способов сделать это - сделать свой собственный класс, который дает правильную оптику для используемого вами типа:

{-# LANGUAGE MultiParamTypeClasses  #-}
{-# LANGUAGE RankNTypes             #-}

class Ocular f t where
  optic :: LensLike f (t a) (t b) a b

instance Settable f => Ocular f ((->) a) where
  optic = mapped

instance Functor f => Ocular f Identity where
  optic = _Wrapped

instance Applicative f => Ocular f [] where
  optic = traversed

instance Applicative f => Ocular f Maybe where
  optic = _Just

Это даст сеттер для (->) s и обход для [] и т.д.

> let maybeTContents = _Wrapped . optic . _Just
> MaybeT [Just 1, Nothing, Just 2] ^.. maybeTContents
[1,2]
> runMaybeT (MaybeT (Just . ('e':)) & maybeTContents %~ ('H':)) "llo"
Just "Hello"

Вы также можете записать экземпляр для MaybeT и ReaderT:

instance (Applicative f, Ocular f m) => Ocular f (MaybeT m) where
  optic = _Wrapped . optic . _Just


instance (Ocular f m, Settable f) => Ocular f (ReaderT r m) where
  optic = _Wrapped . mapped . optic

> MaybeT [Just 1, Nothing, Just 2] ^.. optic
[1,2]
> runReaderT (ReaderT (\r -> [r,r+1]) & optic *~ 2) 1
[2,4]

Обратите внимание, что случай Identity - это только Lens, а не Iso. Для этого вам нужно включить Profuctor в класс Ocular. Вы также можете написать версию, которая позволяет индексировать объектив и обходы таким образом.

Ответ 2

Да! Прежде всего следует отметить, что something должен иметь тип Setter (и, не ограничивая общности, Setter'). Что касается того, какой тип использовать отверстия.

maybeTContents :: Setter' (MaybeT m a) a
maybeTContents =
  _Wrapped . _ . _Just

GHC говорит нам, что хочет тип Settable f => (Maybe a -> f (Maybe a)) -> (m (Maybe a) -> f (m (Maybe a)) для отверстия.

С поездкой в ​​Hackage мы распознаем этот тип как Setter' (m (Maybe a)) (Maybe a). Итак, фиксируя u ~ Maybe a, мы можем более подробно перефразировать вопрос: существует ли сеттер, который объединяется с Setter' [u] u и Setter' (Reader u) u?

Но, поскольку оба [] и Reader имеют экземпляры functor, мы можем обратиться к абсолютному классику setter mapped, сеттер, услышанный по всему миру. mapped имеет тип mapped :: Functor f => Setter (f a) (f b) a b - получается, когда у вас есть экземпляр-функтор, который mapped = sets fmap является значением, которое подчиняется всем законам setter.

Мы видим это в действии здесь:

  % stack ghci
GHCi, version 7.10.3: http://www.haskell.org/ghc/  :? for help
Ok, modules loaded: none.
λ> import Control.Lens
λ> import Control.Monad.Trans.Maybe
λ> import Control.Monad.Trans.Reader
λ> MaybeT [Just 1, Nothing, Just 2, Nothing, Just 3] & _Wrapped . mapped . _Just .~ 100
MaybeT [Just 100,Nothing,Just 100,Nothing,Just 100]
λ> data A = A
λ> data B = B
λ> :t MaybeT (ReaderT (\r -> Identity (Just A)))
MaybeT (ReaderT (\r -> Identity (Just A)))
  :: MaybeT (ReaderT r Identity) A
λ> :t MaybeT (ReaderT (\r -> Identity (Just A))) & _Wrapped . mapped . _Just .~ B
MaybeT (ReaderT (\r -> Identity (Just A))) & _Wrapped . mapped . _Just .~ B
  :: MaybeT (ReaderT r Identity) B

Поскольку не было экземпляра Show для ReaderT, лучшее, что я мог бы сделать, чтобы проиллюстрировать, что работающий сеттер работал, состоял в том, чтобы создать два типа brand-spankin'-new A и B.

Этот вопрос велик, я думаю, потому что он лежит в основе мотивации пакета lens. Учитывая fmapDefault из мира Traversable, вы можете зафиксировать пройденный путь Identity для записи over. Затем вы можете записать инверсию over, sets, такую, что over . sets = id и sets . over = id. Затем мы вынуждены заключить, что mapped = sets fmap является естественным сеттером, который подчиняется законам, которые мы хотим для сеттеров, одним из наиболее важных из которых является mapped . mapped . mapped с (.). Остальная часть lens скоро следует.

Ответ 3

Несколько примеров, основанных на предыдущих ответах:

> MaybeT [Just 1, Nothing, Just 2] ^.. _Wrapped . traverse . _Just
[1,2]
> runMaybeT (MaybeT (Just . ('e':)) & _Wrapped . collect . _Just %~ ('H':)) "llo"
Just "Hello"

т.е. для Traversal/Fold мы используем traverse для Setter: collect (или mapped).

К сожалению, Traversable и Distributive не имеют всех экземпляров: (->) r не Traversable и Const не Distributive (и они не могут быть AFAICS).

Если вы подумаете об этом, вы увидите, что это имеет смысл. Traversal и Distributive являются дуальными, se, чтобы "идти в другом направлении" traverse, мы используем collect.