Для любого аппликативного,
a <$> b <*> c = liftA2 a b c
Для функций,
liftA2 a b c x
= a (b x) (c x) -- by definition;
= (a . b) x (c x)
= ((a <$> b) <*> c) x
Таким образом
(+) <$> (+3) <*> (*100) $ 5
=
liftA2 (+) (+3) (*100) 5
=
(+) ((+3) 5) ((*100) 5)
=
(5+3) + (5*100)
(длинная версия этогоответ следует.)
У чистой математики нет времени.Чистый Хаскелл не имеет времени.Говорить в глаголах («аппликативный функтор применяется » и т. Д.) Может сбивать с толку (« применяется ... когда? ...»).
Вместо (<*>)
- это комбинатор, который объединяет «вычисление» (обозначаемое аппликативным функтором), несущее функцию ( в некотором контексте ), и «вычисление» того же типа, несущее значение ( вкак контекст ), в одно объединенное «вычисление», которое выполняет применение этой функции к этому значению ( в таком контексте ).
«Вычисление» используется для противопоставления этомус чистым Haskell "расчетами".«Вычисления» могут быть или не быть чистыми сами по себе, это ортогональный вопрос.Но в основном это означает, что «вычисления» включают в себя обобщенный протокол вызова функций .Они могут «делать» что-то помимо / как часть / выполнения приложения функции к ее аргументу.Или в типах:
( $ ) :: (a -> b) -> a -> b
(<$>) :: (a -> b) -> f a -> f b
(<*>) :: f (a -> b) -> f a -> f b
(=<<) :: (a -> f b) -> f a -> f b
С функциями контекст является приложением ( другой один), а для восстановления значения - будь то функция или аргумент - приложение для общий аргумент .
(потерпите меня, мы почти у цели).Шаблон a <$> b <*> c
также можно выразить как liftA2 a b c
.Итак, тип «вычисления» аппликативного функтора «functions» определяется как
liftA2 h x y s = let x' = x s -- embellished application of h to x and y
y' = y s in -- in context of functions, or Reader
h x' y'
-- liftA2 h x y = let x' = x -- non-embellished application, or Identity
-- y' = y in
-- h x' y'
-- liftA2 h x y s = let (x',s') = x s -- embellished application of h to x and y
-- (y',s'') = y s' in -- in context of
-- (h x' y', s'') -- state-passing computations, or State
-- liftA2 h x y = let (x',w) = x -- embellished application of h to x and y
-- (y',w') = y in -- in context of
-- (h x' y', w++w') -- logging computations, or Writer
-- liftA2 h x y = [h x' y' | -- embellished application of h to x and y
-- x' <- x, -- in context of
-- y' <- y ] -- nondeterministic computations, or List
-- ( and for Monads we define `liftBind h x k =` and replace `y` with `k x'`
-- in the bodies of the above combinators; then liftA2 becomes liftBind: )
-- liftA2 :: (a -> b -> c) -> f a -> f b -> f c
-- liftBind :: (a -> b -> c) -> f a -> (a -> f b) -> f c
-- (>>=) = liftBind (\a b -> b) :: f a -> (a -> f b) -> f b
Действительно,
> :t let liftA2 h x y r = h (x r) (y r) in liftA2
:: (a -> b -> c) -> (t -> a) -> (t -> b) -> (t -> c)
> :t liftA2 -- the built-in one
liftA2 :: Applicative f => (a -> b -> c) -> f a -> f b -> f c
, то есть типы совпадают, когда мы берем f a ~ (t -> a) ~ (->) t a
, то есть f ~ (->) t
.
Итак, мы уже там :
(+) <$> (+3) <*> (*100) $ 5
=
liftA2 (+) (+3) (*100) 5
=
(+) ((+3) 5) ((*100) 5)
=
(+) (5+3) (5*100)
=
(5+3) + (5*100)
Это просто, как liftA2
определено для этого типа ,Applicative ((->) t) => ...
:
instance Applicative ((->) t) where
pure x t = x
liftA2 h x y t = h (x t) (y t)
Нет необходимости определять (<*>)
. Исходный код говорит :
Минимальное полное определение
pure, ((<*>) | liftA2)
Итак, вы былижелая долго спрашивать, почему это то, что a <$> b <*> c
эквивалентно liftA2 a b c
?
Краткий ответ, это просто так.Один может быть определен в терминах другого - т.е. (<*>)
может быть определен через liftA2
,
g <*> x = liftA2 id g x -- i.e. (<*>) = liftA2 id = liftA2 ($)
-- (g <*> x) t = liftA2 id g x t
-- = id (g t) (x t)
-- = (id . g) t (x t) -- = (id <$> g <*> x) t
-- = g t (x t)
(что в точности соответствует определению в источнике ),
и каждый законный функтор должен следовать закону, который h <$> g = pure h <*> g
.
И наконец,
liftA2 h g x == pure h <*> g <*> x
-- h g x == (h g) x
, поскольку <*>
ассоциируется слева:это infixl 4 <*>
.