Каким будет "отличный метод", который есть у Traversable в дополнение к Foldable?
Foldable
это суперкласс Traversable
аналогично тому, как Functor
это суперкласс Applicative
а также Monad
,
Аналогично случаю Monad
где можно в принципе реализовать fmap
как
liftM :: Monad m => (a->b) -> m a -> m b
liftM f q = return . f =<< q
мы могли бы также подражать foldMap
как
foldLiftT :: (Traversable t, Monoid m) => (a -> m) -> t a -> m
foldLiftT f = fst . traverse (f >>> \x -> (x,x))
-- or: . sequenceA . fmap (f >>> \x -> (x, x))
с использованием Monoid m => (,) m
монада. Таким образом, сочетание суперкласса и методов в обоих случаях несет определенную избыточность.
В случае монад можно утверждать, что "лучшим" определением класса типов будет (я пропущу аппликативный / моноидальный)
class (Functor m) => Monad m where
return :: a -> m a
join :: m (m a) -> m a
по крайней мере, это то, что используется в теории категорий. Это определение делает, не используя Functor
суперкласс, не разрешать liftM
так что без этой избыточности.
Возможно ли подобное преобразование для Traversable
учебный класс?
Чтобы уточнить: что мне нужно, это переопределение, давайте назовем это,
class (Functor t, Foldable t) => Traversable t where
skim :: ???
так что мы могли бы сделать фактический Traverse
методы функций верхнего уровня
sequenceA :: (Traversable t, Applicative f) => t (f a) -> f (t a)
но было бы невозможно сделать в общем
instance (Traversable t) => Foldable t where
foldMap = ... skim ...
data T
instance Traversable T where
skim = ...
Я не спрашиваю, потому что мне нужно это для чего-то конкретного; это концептуальный вопрос, чтобы лучше понять разницу между Foldable
а также Traversable
, Снова очень похоже Monad
против Functor
: в то время как >>=
гораздо удобнее, чем join
для повседневного программирования на Haskell (потому что вам обычно нужна именно эта комбинация fmap
а также join
), последнее облегчает понимание того, что такое монада.
2 ответа
Foldable
это к Functor
как Traversable
это к Monad
т.е. Foldable
а также Functor
суперклассы Monad
а также Traversable
(по модулю все аппликативный / монадный шум предложения).
Действительно, это уже в коде
instance Foldable f => Traversable f where
...
Так что не ясно, что еще нужно хотеть. Foldable
характеризуется toList :: Foldable f => f a -> [a]
в то время как Traversable
в конечном итоге зависит не только от возможности абстрагировать контент в виде списка toList
делает, но и иметь возможность извлечь форму
shape :: Functor f => f a -> f ()
shape = fmap (const ())
а затем рекомбинировать их
combine :: Traversable f => f () -> [a] -> Maybe (f a)
combine f_ = evalStateT (traverse pop f_) where
pop :: StateT [a] Maybe a
pop = do x <- get
case x of
[] = empty
(a:as) = set as >> return a
который зависит от traverse
,
Для получения дополнительной информации об этой собственности см. Этот пост в блоге Рассел О'Коннор.
Супер волнистые, потому что уже поздно, но дополнительная сила, которая Traversable
имеет более Foldable
это способ восстановить первоначальную структуру. Например, со списками:
module MyTraverse where
import Data.Foldable
import Data.Traversable
import Control.Applicative
import Data.Monoid
data ListRec f x = ListRec
{ el :: f (Endo [x])
}
instance Applicative f => Monoid (ListRec f x) where
mempty = ListRec (pure mempty)
mappend (ListRec l) (ListRec r) =
ListRec (mappend <$> l <*> r)
toM :: Functor f => f b -> ListRec f b
toM this = ListRec $ (Endo . (:)) <$> this
fromM :: Functor f => ListRec f b -> f [b]
fromM (ListRec l) = flip appEndo [] <$> l
myTraverse :: Applicative f => (a-> f b) -> [a] -> f [b]
myTraverse f xs = fromM $ foldMap (toM . f) xs
я думаю это myTraverse
ведет себя так же, как traverse
, используя только классы Applicative
, Foldable
, а также Monoid
, Вы можете переписать его, чтобы использовать foldr
вместо foldMap
если ты хотел избавиться от Monoid
,
списки просты, потому что они плоская структура. Тем не менее, я сильно подозреваю, что вы можете использовать Zipper, чтобы получить правильную функцию восстановления для любой структуры (так как молнии являются производными в общем, они всегда должны существовать).
Но даже с застежкой-молнией у вас нет никакого способа указать эту структуру моноиду / функции. По идее, похоже Traversable
добавляет что-то вроде
class Traversed t where
type Path t :: *
annotate :: t a -> [(Path t, a)]
fromKeyed :: [(Path t, a)] -> t a
это, кажется, сильно пересекается с Foldable
, но я думаю, что это неизбежно при попытке связать пути с их составляющими ценностями.