Как sequenceA работает со списками пар?

Oct 03 2020

Ответьте на этот вопрос. Интуитивно я понял, что sequenceAделает этот вариант использования, но не понял , как / почему он так работает.

Итак, все сводится к вопросу: как это sequenceAработает в следующем случае?

> sequenceA [("a",1),("b",2),("c",3)]
("abc",[1,2,3])

я вижу это

sequenceA :: (Traversable t, Applicative f) => t (f a) -> f (t a)

так что в примере использования выше Traversableis []и Applicative, поскольку (,)конструктор двоичного типа, is (,) a, что означает, что пара берется как аппликативный функтор в своем sndполе. И этот список попадает sndв результат. Итак, мы переходим от списка пар к паре со списком во втором поле.

Но откуда "abc"взялось? Я имею в виду, я знаю, что это конкатенация fstвсех пар, но я не знаю, через ++или через concatсписок fsts. Похоже, что в sequenceAподписи пользователя нет ничего, fstчто требовало бы совмещения s пар.

Тем не менее, это предположение нужно где-то использовать. Действительно, следующее неверно

sequenceA [('a',1),('b',2),('c',3)]

Ответы

5 danidiaz Oct 03 2020 at 02:46

В Haskell экземпляры класса типов для типа могут быть «обусловлены» существованием других экземпляров класса типов для частей типа. Не все конструкторы типа формы ((,) a)являются экземплярами Applicative, а только те, для которых aтип имеет Monoidэкземпляр.

Эти обязательные ограничения появляются перед =>Haddocks экземпляра, например:

Monoid a => Applicative ((,) a)

Зачем Monoidнужен экземпляр? Во -первых , pureдля ((,) a)необходимости материализовать aзначение из воздуха, чтобы поместить в первый элемент кортежа. memptyдля типа aделает свою работу.


Могут существовать цепочки требуемых ограничений на несколько уровней. Например, почему работает следующее?

ghci> import Datta.Function ((&)) -- flipped application
ghci> [(id :: String -> String, 2 :: Int), (\x -> x ++ x, 1)] & sequenceA & fst $ "foo"
"foofoofoo"

Здесь первый компонент - это функция. Как и раньше, для работы у него должен быть Monoidэкземпляр sequenceA. Но когда это тип ? Глядя на пикши, мы находим:a -> bMonoid

Monoid b => Monoid (a -> b)

То есть функции - это Monoids, когда возвращаемый тип (здесь String) - Monoid.

Фактически, есть еще один Monoid экземпляр функций, доступных через Endonewtype. Новые типы обычно используются для выбора того, какой экземпляр использовать для данной операции, хотя для этого требуется некоторое количество упаковки и развертывания.

7 DanielWagner Oct 03 2020 at 02:18

Он использует mappend. ApplicativeЭкземпляр он использует выглядит следующим образом :

instance Monoid a => Applicative ((,) a) where
    pure x = (mempty, x)
    (af, f) <*> (ax, x) = (mappend af ax, f x)