Поиск…


Вступление

Applicative - это класс типов f :: * -> * который позволяет использовать приложение отмененной функции над структурой, в которой функция также встроена в эту структуру.

замечания

Определение

class Functor f => Applicative f where
    pure  :: a -> f a
    (<*>) :: f (a -> b) -> f a -> f b

Обратите внимание на ограничение Functor на f . pure функция возвращает свой аргумент, встроенный в Applicative структуру. Функция infix <*> (произносится как «apply») очень похожа на fmap за исключением функции, встроенной в Applicative структуру.

Правильный экземпляр Applicative должен удовлетворять прикладным законам , хотя они не применяются компилятором:

pure id <*> a = a                              -- identity
pure (.) <*> a <*> b <*> c = a <*> (b <*> c)   -- composition
pure f <*> pure a = pure (f a)                 -- homomorphism
a <*> pure b = pure ($ b) <*> a                -- interchange

Альтернативное определение

Поскольку каждый прикладной функтор является функтором , fmap всегда можно использовать на нем; таким образом, сущность Аппликативного - это сопряжение несущего содержимого, а также способность его создавать:

class Functor f => PairingFunctor f where
  funit :: f ()                  -- create a context, carrying nothing of import
  fpair :: (f a,f b) -> f (a,b)  -- collapse a pair of contexts into a pair-carrying context

Этот класс изоморфен Applicative .

pure a = const a <$> funit = a <$ funit  

fa <*> fb = (\(a,b) -> a b) <$> fpair (fa, fb) = uncurry ($) <$> fpair (fa, fb)

Наоборот,

funit = pure ()

fpair (fa, fb) = (,) <$> fa <*> fb

Общие примеры применения

Может быть

Maybe , это аппликативный функтор, содержащий возможно отсутствующее значение.

instance Applicative Maybe where
    pure = Just
    
    Just f <*> Just x = Just $ f x
    _ <*> _ = Nothing

pure поднимает данное значение в Maybe , применяя Just к нему. Функция (<*>) применяет функцию, завернутую в « Maybe к значению в « Maybe . Если присутствуют как функция, так и значение (построено с помощью Just ), функция применяется к значению, и возвращается завернутый результат. Если либо отсутствует, вычисление не может продолжаться, и вместо него Nothing не возвращается.

Списки

Один из способов, чтобы списки соответствовали сигнатуре типа <*> :: [a -> b] -> [a] -> [b] - это декартово произведение двух списков, объединяющее каждый элемент первого списка с каждым элемент второго:

fs <*> xs = [f x | f <- fs, x <- xs]
         -- = do { f <- fs; x <- xs; return (f x) }

pure x = [x]

Обычно это интерпретируется как эмулирующий недетерминизм со списком значений, стоящих для недетерминированного значения, возможные значения которого находятся над этим списком; поэтому комбинация двух недетерминированных значений охватывает все возможные комбинации значений в двух списках:

ghci> [(+1),(+2)] <*> [3,30,300]
[4,31,301,5,32,302]

Бесконечные потоки и zip-списки

Существует класс Applicative который «застегивает» свои два входа вместе. Один простой пример - бесконечные потоки:

data Stream a = Stream { headS :: a, tailS :: Stream a }

Applicative экземпляр Stream применяет поток функций к потоку аргументов по-разному, сопоставляя значения в двух потоках по положению. pure возвращает постоянный поток - бесконечный список одного фиксированного значения:

instance Applicative Stream where
    pure x = let s = Stream x s in s
    Stream f fs <*> Stream x xs = Stream (f x) (fs <*> xs)

Списки также допускают «zippy» Applicative экземпляр, для которого существует ZipList :

newtype ZipList a = ZipList { getZipList :: [a] }

instance Applicative ZipList where
    ZipList xs <*> ZipList ys = ZipList $ zipWith ($) xs ys

Поскольку zip обрезает свой результат в соответствии с самым коротким входом, единственная реализация pure которая удовлетворяет Applicative законам, - это та, которая возвращает бесконечный список:

    pure a = ZipList (repeat a)   -- ZipList (fix (a:)) = ZipList [a,a,a,a,...

Например:

ghci> getZipList $ ZipList [(+1),(+2)] <*> ZipList [3,30,300]
[4,32]

Две возможности напоминают нам внешний и внутренний продукт, аналогичный умножению матрицы 1 столбца ( nx 1 ) с 1-строчной ( 1 xm ) в первом случае, в результате получается матрица nxm (но сплющенная ); или умножения 1-строчной и 1-столбцовой матриц (но без суммирования) во втором случае.

функции

Когда они специализированы по функциям (->) r , сигнатуры типа pure и <*> соответствуют S комбинаторов K и S соответственно:

pure :: a -> (r -> a)
<*> :: (r -> (a -> b)) -> (r -> a) -> (r -> b)

pure должен быть const , а <*> принимает пару функций и применяет их каждый к фиксированному аргументу, применяя два результата:

instance Applicative ((->) r) where
    pure = const
    f <*> g = \x -> f x (g x)

Функции - прототипный «zippy» аппликативный. Например, поскольку бесконечные потоки изоморфны (->) Nat , ...

-- | Index into a stream
to :: Stream a -> (Nat -> a)
to (Stream x xs) Zero = x
to (Stream x xs) (Suc n) = to xs n

-- | List all the return values of the function in order
from :: (Nat -> a) -> Stream a
from f = from' Zero
    where from' n = Stream (f n) (from' (Suc n))

... представление потоков по более высокому порядку создает автоматически Applicative экземпляр zippy.



Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow