在Haskell中,为什么Applicatives需要在同一个上下文中接受态射和数据?

我是Haskell的新手。这可能是愚蠢的问题。

由于Applicative类型类具有 apply 函数,该函数在相同的上下文中使用这些函数和数据。为什么它不能与众不同,却更通用。

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

为什么我们不能写这样的东西

class Functor f => Applicative f where
    (<*>) :: Functor g => g (a -> b) -> f a -> g (f b)
    (<*>) gab fa = fmap (\g -> fmap g fa) gab

    (<<*>>) :: Functor g => (g (f a) -> f a) -> g (a -> b) -> f a -> f b
    (<<*>>) peelOuter gab fa = peelOuter $ gab <*> fa

    (>>*<<) :: Functor g => (g (f a) -> g a) -> g (a -> b) -> f a -> g b
    (>>*<<) cleanInner gab fa = cleanInner $ gab <*> fa

它可以如下使用

-- Extract List from maybe
elfm :: Maybe [a] -> [a]
elfm Nothing = []
elfm (Just xs) = xs

-- Fuse List elements in Maybe []
flem :: Monoid a => Maybe [a] -> Maybe a
flem Nothing = mempty
flem (Just xs) = Just $ foldl (<>) mempty xs

Just (*2) <*> [1,2,3,4]
-- Just [2,4,6,8]
(<<*>>) elfm (Just (*2)) [1,4]
-- [2,8]
(>>*<<) flem (Just (++ "Haskell")) ["Hello,"]
-- Just "Hello,Haskell"

我读到,拥有Applicatives的全部要点是Functors提升多参数函数的缺点。是这样吗?

我认为功能应用程序不符合预期。

add :: Num a => a -> a -> a
add a b = a + b

-- I want to apply [1,3] as First arguments and [4,5,6] as 2nd arguments.
-- Like add 1 4,add 2 4,add 3 6
-- But it is give all possibilities of combinations like a tree

--                          <*>
--      (+1)                (+2)            (+3)
-- (1+4)(1+5)(1+6)  (2+4)(2+5)(2+6)  (3+4)(3+5)(3+6)

还将它们与批处理进行比较,但没有给出非常真实的示例。请为此提供示例。

H42801 回答:在Haskell中,为什么Applicatives需要在同一个上下文中接受态射和数据?

Applicative的每个实例都必须具有自己的<*>实现。这就是为什么我们首先要有类型类的原因。您的代码具有类本身定义的所有方法,实例一无所有。这意味着根本没有类型类。只有一堆通用函数。所有的肉都委托给peelOutercleanInner的参数('<<*>>)(>>*<<)。让我们更仔细地研究它们。它们或多或少是对称的,因此(<<*>>)应该足够。

(<<*>>) :: Functor g => (g (f a) -> f a) -> g (a -> b) -> f a -> f b
(<<*>>) peelOuter gab fa = peelOuter $ gab <*> fa

实际上peelOuter应该是类型类的方法,但是存在多个问题。

第一个问题涉及两个函子,并且每个{em>对函子必须分别实现peelOuter。也就是说,这里将有一个双参数类型类ApplicativePair,并且每对都需要一个单独的实例。

第二个问题是peelOuter不能用于每对善意的Applicative仿函数。不能从Id a提取Maybe (Id a)或从[a]提取IO [a]或...

更糟糕的是,当fg是同一个函子时,尚不清楚它是否始终可以实现。显然,如果f是单子,那么它只是join。但是,并非所有的求助词都是monad,而join恰恰是一个求助词所缺少的monad。因此,peelOuter即使这种类型是可实现的,也会违反某些monad法则。那是一件坏事?如果仍遵循适用法律,则不一定。但是,您没有提供任何法律,只有一堆功能。

,

任何两个函子是 Functor Applicative 函子。该代码使用newtype Compose进行编码,请参见Data.Functor.Compose

因此,您的示例可以通过newtype Compose来解决。

-- Just (*2) <*> [1,2,3,4]
getCompose $ pure (*2) <*> Compose (Just [1,4])
-- or
getCompose $ (*2) <$> Compose (Just [1,4])
-- Just [2,4,6,8]

-- (<<*>>) elfm (Just (*2)) [1,4]
elfm . getCompose $ pure (*2) <*> Compose (Just [1,4])
-- or with toList (method of Foldable)
toList $ pure (*2) <*> Compose (Just [1,4])
-- or
toList $ (*2) <$> Compose (Just [1,4])
-- [2,8]

-- (>>*<<) flem (Just (++ "Haskell")) ["Hello,"]
flem . getCompose $ pure (++ "Haskell") <*> Compose (Just ["Hello,"])
-- or with toList and listToMaybe
listToMaybe . toList $ pure (++ "Haskell") <*> Compose (Just ["Hello,"])
-- or
listToMaybe . toList $ (++ "Haskell") <$> Compose (Just ["Hello,"])
-- or with head :: Foldable f => f a -> Maybe a
head $ (++ "Haskell") <$> Compose (Just ["Hello,"])
-- Just "Hello,Haskell"

关于最后一个问题。 @Robin Zigmond在评论中提供了答案。它写了newtype ZipList。使用ZipList,您可以做到:

getZipList $ (+) <$> ZipList [1,3] <*> ZipList [4,5,6]
-- [5,7,9]

因此,Haskell中newtype的目的之一是能够为某种类型编写不同的实例。

本文链接:https://www.f2er.com/3147467.html

大家都在问