Haskell - Определен ли порядок действия в случае аппликативного?
При выполнении действия IO, определенного someFun <$> (a :: IO ()) <$> (b :: IO ())
, выполняется ли выполнение действий a
и b
? То есть, могу ли я рассчитывать на то, что a
выполняется до b
?
Для GHC я вижу, что IO реализовано с использованием State, а также см. здесь, что это аппликативный экземпляр, но не может найдите источник фактической декларации экземпляра. Выполнение через государство предполагает, что различные IO-эффекты должны быть последовательными, но не обязательно определяют их порядок.
Игра в GHCi кажется, что Appliative сохраняет порядок эффекта, но является ли это какой-то универсальной гарантией или спецификой GHC? Мне было бы интересно узнать подробности.
import System.Time
import Control.Concurrent
import Data.Traversable
let prec (TOD a b) = b
fmap (map prec) (sequenceA $ replicate 5 (threadDelay 1000 >> getClockTime))
[641934000000,642934000000,643934000000,644934000000,645934000000]
Спасибо!
Ответы
Ответ 1
Это конечно детерминированный, да. Он всегда будет делать то же самое для любого конкретного экземпляра. Тем не менее, нет причин для выбора слева направо справа налево для порядка эффектов.
Однако из документации для Applicative
:
Если f
также является Monad
, оно должно удовлетворять pure
= return
и (<*>)
= ap
(что означает, что pure
и <*>
удовлетворяют законам аппликативного функтора).
Определение ap
таково: от Control.Monad
:
ap :: (Monad m) => m (a -> b) -> m a -> m b
ap = liftM2 id
И liftM2
определяется очевидным образом:
liftM2 f m1 m2 = do { x1 <- m1; x2 <- m2; return (f x1 x2) }
Это означает, что для любого функтора, который является Monad
, а также Applicative
, ожидается (по спецификации, поскольку это невозможно выполнить в коде), что Applicative
будет работа слева направо, так что блок do
в liftM2
выполняет то же самое, что и liftA2 f x y = f <$> x <*> y
.
Из-за вышеприведенного, даже для экземпляров Applicative
без соответствующего Monad
, по соглашению эффекты обычно упорядочиваются также слева направо.
В более широком смысле, поскольку структура вычисления Applicative
обязательно не зависит от "эффектов", вы обычно можете анализировать значение программы независимо от того, как эффекты Applicative
упорядочиваются. Например, если экземпляр для []
был изменен на последовательность справа налево, любой код, использующий его, даст те же результаты, только с элементами списка в другом порядке.
Ответ 2
Да, порядок предопределен соглашением Monad-Applicative. Это легко увидеть: комбинатор (*>)
должен соответствовать комбинатору (>>)
в хорошо выполненном экземпляре Applicative
для монады, и его определение:
a *> b = liftA2 (const id) a b
Другими словами, если b
было выполнено до a
, экземпляр Applicative
будет плохо себя вести.
Изменить: как примечание: это явно не указано нигде, но вы можете найти много других подобных соответствий, таких как liftM2
= liftA2
и т.д.
Ответ 3
Для IO Applicative это, безусловно, так. Но посмотрите асинхронный пакет для примера аппликативного, где в f <$> a <*> b
эффекты a
и b
происходят параллельно.