Ответ 1
Извинения за лаконичный и механический ответ. Мне не нравятся вишневые вещи, такие как Аппликативные или Монады, но я не знаю, где вы находитесь. Это не мой обычный подход к обучению Haskell.
Во-первых, ap
действительно (<*>)
под капотом.
Prelude> import Control.Monad
Prelude> import Data.Maybe
Prelude> import Control.Applicative
Prelude> :t ap
ap :: Monad m => m (a -> b) -> m a -> m b
Prelude> :t (<*>)
(<*>) :: Applicative f => f (a -> b) -> f a -> f b
Что это значит? Это означает, что нам не нужно что-то "сильное", как Монада, чтобы описать, что мы делаем. Достаточно аппликативного. Однако Functor не делает.
Prelude> :info Applicative
class Functor f => Applicative (f :: * -> *) where
pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b
Prelude> :info Functor
class Functor (f :: * -> *) where
fmap :: (a -> b) -> f a -> f b
Здесь ap
/(<*>)
с предположительно монадой/аппликацией:
Prelude> ap (Just (+1)) (Just 1)
Just 2
Prelude> (<*>) (Just (+1)) (Just 1)
Just 2
Первое, что нужно выяснить, - это какой экземпляр класса Applicative, о котором мы говорим?
Prelude> :t fromMaybe
fromMaybe :: a -> Maybe a -> a
Desugaring fromMaybe типа немного дает нам:
(->) a (Maybe a -> a)
Итак, конструктор типа, с которым мы здесь связаны, (->)
. Что говорит GHCi о (->)
, также известном как типы функций?
Prelude> :info (->)
data (->) a b -- Defined in ‘GHC.Prim’
instance Monad ((->) r) -- Defined in ‘GHC.Base’
instance Functor ((->) r) -- Defined in ‘GHC.Base’
instance Applicative ((->) a) -- Defined in ‘GHC.Base’
Хмм. Что может быть?
Prelude> :info Maybe
data Maybe a = Nothing | Just a -- Defined in ‘GHC.Base’
instance Monad Maybe -- Defined in ‘GHC.Base’
instance Functor Maybe -- Defined in ‘GHC.Base’
instance Applicative Maybe -- Defined in ‘GHC.Base’
Что случилось с использованием (<*>)
для Maybe, было следующее:
Prelude> (+1) 1
2
Prelude> (+1) `fmap` Just 1
Just 2
Prelude> Just (+1) <*> Just 1
Just 2
Prelude> :t fmap
fmap :: Functor f => (a -> b) -> f a -> f b
Prelude> let mFmap = fmap :: (a -> b) -> Maybe a -> Maybe b
Prelude> (+1) `mFmap` Just 1
Just 2
Prelude> :t (<*>)
(<*>) :: Applicative f => f (a -> b) -> f a -> f b
Prelude> let mAp = (<*>) :: Maybe (a -> b) -> Maybe a -> Maybe b
Prelude> :t (+1)
(+1) :: Num a => a -> a
Prelude> :t Just (+1)
Just (+1) :: Num a => Maybe (a -> a)
Prelude> Just (+1) `mAp` Just 1
Just 2
Хорошо, как насчет функционального типа Functor и Applicative? Одна из сложнейших частей здесь состоит в том, что (->)
должен быть частично применен в типе как Functor/Applicative/Monad. Таким образом, ваш f
становится (->) a
общего (->) a b
, где a
- тип аргумента, а b
- результат.
Prelude> (fmap (+1) (+2)) 0
3
Prelude> (fmap (+1) (+2)) 0
3
Prelude> :t fmap
fmap :: Functor f => (a -> b) -> f a -> f b
Prelude> let funcMap = fmap :: (a -> b) -> (c -> a) -> c -> b
Prelude> -- f ~ (->) c
Prelude> (funcMap (+1) (+2)) 0
3
Prelude> :t (<*>)
(<*>) :: Applicative f => f (a -> b) -> f a -> f b
Prelude> let funcAp = (<*>) :: (c -> a -> b) -> (c -> a) -> (c -> b)
Prelude> :t fromMaybe
fromMaybe :: a -> Maybe a -> a
Prelude> :t funcAp fromMaybe
funcAp fromMaybe :: (b -> Maybe b) -> b -> b
Prelude> :t const
const :: a -> b -> a
Prelude> :t funcAp const
funcAp const :: (b -> b1) -> b -> b
Не гарантируется быть полезным. Вы можете сказать, что funcAp const
не интересен только с типом и знает, как работает параметричность.
Изменить: говоря о compose, Functor для (->) a
- это просто (.)
. Аппликация такова, но с дополнительным аргументом. Монада является аппликативным, но с аргументами перевернуты.
Дальнейшая кинетика: Аппликативная <*>
для (->) a
) равна S, а pure
- K вычисления комбинатора SKI. (Вы можете получить я из K и S. Фактически вы можете получить любую программу из K и S.)
Prelude> :t pure
pure :: Applicative f => a -> f a
Prelude> :t const
const :: a -> b -> a
Prelude> :t const
const :: a -> b -> a
Prelude> let k = pure :: a -> b -> a
Prelude> k 1 2
1
Prelude> const 1 2
1