Составление аппликаций - PullRequest
       120

Составление аппликаций

1 голос
/ 23 сентября 2019

Я читаю главу 25 (Составление типов) из haskellbook и хочу более полно понять аппликативную композицию

Автор предоставляет тип для воплощения композиции типов:

newtype Compose f g a =
  Compose { getCompose :: f (g a) }
  deriving (Eq, Show)

и предоставляет экземпляр функтора для этого типа:

instance (Functor f, Functor g) =>
  Functor (Compose f g) where
    fmap f (Compose fga) =
      Compose $ (fmap . fmap) f fga

Но экземпляр Applicative оставлен читателю в качестве упражнения:

instance (Applicative f, Applicative g) =>
  Applicative (Compose f g) where
    -- pure :: a -> Compose f g a
    pure = Compose . pure . pure
    -- (<*>) :: Compose f g (a -> b)
    --       -> Compose f g a
    --       -> Compose f g b
    Compose fgf <*> Compose fgx = undefined

Я могу обманутьи найдите ответ в Интернете ... Источник Data.Functor.Compose предоставляет определение аппликативного экземпляра:

Compose f <*> Compose x = Compose ((<*>) <$> f <*> x)

, но у меня возникают проблемы с пониманием того, что здесь происходит.Согласно сигнатурам типа f и x заключены в два слоя аппликативной структуры.Дорожный блок, на который я, кажется, нахожусь, хотя понимает, что происходит с этим битом: (<*>) <$> f.У меня, вероятно, будут дополнительные вопросы, но они, вероятно, зависят от того, как оценивается это выражение.Это говорит «fmap <*> over f» или «применить <$> к f»?

Пожалуйста, помогите прийти к интуитивному пониманию того, что здесь происходит.

Спасибо!:)

Ответы [ 2 ]

2 голосов
/ 23 сентября 2019

Вместо <*> вы можете определить liftA2.

import Control.Applicative (Applicative (..))

newtype Compose f g a = Compose
  { getCompose :: f (g a) }
  deriving Functor

instance (Applicative f, Applicative g) => Applicative (Compose f g) where
  pure a = Compose (pure (pure a))
  -- liftA2 :: (a -> b -> c) -> Compose f g a -> Compose f g b -> Compose f g c
  liftA2 f (Compose fga) (Compose fgb) = Compose _1

У нас есть fga :: f (g a) и fgb :: f (g b), и нам нужно _1 :: f (g c).Поскольку f является аппликативным, мы можем объединить эти два значения, используя liftA2:

  liftA2 f (Compose fga) (Compose fgb) = Compose (liftA2 _2 fga fgb)

Теперь нам нужно

_2 :: g a -> g b -> g c

Поскольку g также аппликативен, мы можем использоватьliftA2 также:

  liftA2 f (Compose fga) (Compose fgb) = Compose (liftA2 (liftA2 f) fga fgb)

Этот способ отмены liftA2 приложений полезен и для других вещей.Вообще говоря,

liftA2 . liftA2 :: (Applicative f, Applicative g) => (a -> b -> c) -> f (g a) -> f (g b) -> f (g c)

liftA2 . liftA2 . liftA2
  :: (Applicative f, Applicative g, Applicative h)
  => (a -> b -> c) -> f (g (h a)) -> f (g (h b)) -> f (g (h c))
1 голос
/ 23 сентября 2019

Рассмотрим выражение a <$> b <*> c.Это значит взять функцию a и отобразить ее на функторе b, который даст новый функтор, а затем отобразить этот новый функтор на функторе c.

Сначала представьте, что a равно (\x y -> x + y), b равно Just 3, а c равно Just 5.a <$> b затем оценивается в Just (\y -> 3 + y), а a <$> b <*> c затем оценивается в Just 8.

(Если то, что здесь раньше, не имеет смысла, то вам следует попытаться понять отдельные слои аппликативных программ, прежде чемвы пытаетесь понять их несколько слоев.)

Аналогично, в вашем случае a - это (<*>), b - это f, а c - это x.Если бы вы выбрали подходящие значения для f и x, вы бы увидели, что они также могут быть легко оценены (хотя убедитесь, что ваши слои различны; (<*>) в вашем случае принадлежит внутреннему Applicativeтогда как <$> и <*> принадлежат внешнему).

...