Как sequenceA работает со списками пар?
Ответьте на этот вопрос. Интуитивно я понял, что 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)]
Ответы
В 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. Новые типы обычно используются для выбора того, какой экземпляр использовать для данной операции, хотя для этого требуется некоторое количество упаковки и развертывания.
Он использует mappend. ApplicativeЭкземпляр он использует выглядит следующим образом :
instance Monoid a => Applicative ((,) a) where
pure x = (mempty, x)
(af, f) <*> (ax, x) = (mappend af ax, f x)