Как Traversable использует тот факт, что он является подклассом как Foldable, так и Functor?

class (Functor t, Foldable t) => Traversable t where

    traverse :: Applicative f => (a -> f b) -> t a -> f (t b)
    traverse g = sequenceA . fmap g

    sequenceA :: Applicative f => t (f a) -> f (t a)
    sequenceA = traverse id

Как Traversable использует тот факт, что он является подклассом как Foldable, так и Functor?

t быть проходимым типом подразумевает, что t также является типом функтора и складываемым типом.

Я вижу, что тот факт, что t является типом функтора, т.е. fmap, используется в traverse.

Используется ли где-нибудь тот факт, что t - складной тип?

traverse использует тот факт, что t складной тип?

Какой факт sequenceA использует: t тип функтора, t складывающийся тип или и то, и другое?

Можем ли мы определить класс, который является подклассом только Functor и имеет одинаковые функции traverse и sequenceA?

Спасибо.


person Tim    schedule 30.07.2019    source источник


Ответы (3)


Экземпляр Foldable не используется. Тем не менее, это нормально требовать Foldable, поскольку если мы можем traverse вещь, то мы можем foldMap это:

foldMapDefault :: (Traversable t, Monoid m) => (a -> m) -> t a -> m
foldMapDefault f = fst . traverse (\a -> (f a, ()))

Основная идея здесь - использовать стандартную монаду писателя; так как операция связывания для писателя использует mappend для объединения «записанной» части - здесь значения f a - traverse будут mappend вместе только правильные вещи. (Это также создаст t (), о котором мы на самом деле не заботимся; выбросить эту часть - это работа fst.)

Для простоты и самодостаточности я использовал монаду писателя, но настоящая реализация использует немного сложную Const аппликативную форму, чтобы избежать создания (а затем отбрасывания) неинтересного значения t (). Вы можете увидеть документацию foldMapDefault здесь и его реализация здесь .

person Daniel Wagner    schedule 30.07.2019
comment
Что делает Const слишком волшебным? Да, реализация foldMapDefault полна магии принуждения, но это не важно. - person dfeuer; 31.07.2019
comment
@dfeuer Мое мнение, основанное на других вопросах этого пользователя, состоит в том, что хороший ответ для этого пользователя должен представлять одну и только одну новую идею за раз. Превратить обход в складку - это уже одна идея. Я могу только надеяться, что монада писателя уже не была второй идеей, не говоря уже о менее сложном, но и менее популярном Const аппликативе. - person Daniel Wagner; 31.07.2019

Есть две основные причины для создания подклассов в целом:

  1. Вам нужен класс, чтобы ваше определение работало, или, по крайней мере, он делает реализацию настолько ясной, что на самом деле нет смысла оставлять его в стороне. Так обстоит дело с Functor.

  2. Вы можете бесплатно получить другой класс из частей, которые у вас уже есть для вашего основного определения, так что вы также можете объявить его. Так обстоит дело с Foldable.

person Karl Bielefeldt    schedule 30.07.2019
comment
Определенно есть и другие причины для создания суперклассов. Num является суперклассом Integral, потому что нет смысла делать что-то целым, если это не число. Semigroup является (теперь) суперклассом Monoid, потому что моноид - это полугруппа с элементом идентичности (математически). - person dfeuer; 30.07.2019
comment
@dfeuer Отношение полугруппа-моноид, возможно, подпадает под пункт (2) здесь. - person Daniel Wagner; 31.07.2019
comment
@DanielWagner, это чисто историческая случайность. - person dfeuer; 31.07.2019

Из здесь класс Traversable Functor и Foldable, и должны соответствовать законам:

И Foldable дополнительные сведения см. здесь. Это означает, что его можно сложить (foldMap, foldr, foldl ...)

traverse функция должна удовлетворять законам:

  • естественность:

    т. traverse f = traverse (t. f) для каждого аппликативного преобразования t

  • личность

    пройти Идентичность = Идентичность

  • состав

    traverse (Compose. fmap g. f) = Составить. fmap (ход g). траверс f

и последовательность A:

  • естественность

    т. последовательностьA = последовательностьA. fmap t для каждого аппликативного преобразования t

  • личность

    последовательность А. fmap Identity = Идентичность

  • состав

    последовательность А. fmap Compose = Составить. Последовательность fmap A. последовательностьА

Какой факт использует sequenceA: t является типом Functor, t является типом Foldable или и тем, и другим?

Traversable, как говорится в его определении (и в приведенных выше законах):

class (Functor t, Foldable t) => Traversable t where

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

А что на самом деле? Определение, но почему дизайнер Traversable выбрал именно эти два? Потому что это полезно, как вы можете видеть в ответе @Daniel Wagner. Другие примеры:

instance Traversable [] where
    traverse f = List.foldr cons_f (pure [])
      where cons_f x ys = liftA2 (:) (f x) ys

этот использует foldr

instance Foldable [] where
    elem    = List.elem
    foldl   = List.foldl
    foldl'  = List.foldl'
    foldl1  = List.foldl1
    foldr   = List.foldr
    foldr1  = List.foldr1
    length  = List.length
    maximum = List.maximum
    minimum = List.minimum
    null    = List.null
    product = List.product
    sum     = List.sum
    toList  = id

Итак, Traverse - это Functor и Foldable, поэтому вы можете использовать функции его интерфейса при необходимости. (как в примере, это просто пример, а не обоснование того, почему дизайнер решил определить Traversable с Functor и Foldable), потому что это полезно.

person Damián Rafael Lattenero    schedule 30.07.2019