简体   繁体   English

应用函子的目的是什么?

[英]What is the purpose of an applicative functor?

Can anyone share a good real life situation when a function with the following signature would be useful? 当具有以下签名的函数有用时,任何人都可以分享良好的现实生活情况吗?

f (a -> b) -> f a -> f b

I can't really see where I would need something like the schoolbook examples from learn-you-a-haskell [(+),(*)] <*> [1,2] <*> [3,4] 我无法真正看到我需要的东西,比如来自Learn-you-a-haskell [(+),(*)] <*> [1,2] <*> [3,4]的教科书示例

Applicatives can be used for lots of stuff that you might also do with a monad, but that don't really need the stronger features of that class. Applicative可以用于许多你可能也用monad做的东西,但是它们并不真正需要那个类的更强大的功能。 (More precisely, whenever the “shape” of the functor doesn't depend on values within , then Applicative is sufficient.) For instance, an action like (更准确地说,只要仿函数的“形状”不依赖于其中的值 ,那么Applicative就足够了。)例如,像

foobar :: IO [String]
foobar = do
   fooTxt <- readFile "foo.txt"
   barTxt <- readFile "bar.txt"
   return $ zip (lines fooTxt) (lines barTxt)

could as well be written 也可以写

foobar = zip <$> (lines <$> readFile "foo.txt")
             <*> (lines <$> readFile "bar.txt")

In this case, that makes it merely a tad shorter, but in other cases it may also improve performance (because Applicative is less general, more optimisations are possible) or allow you to make code more generic than when you use the Monad interface. 在这种情况下,这使得它只是稍微短一点,但在其他情况下它也可以提高性能(因为Applicative不太通用,可以进行更多优化)或者允许您使代码比使用Monad接口时更通用。

The most common use I have of it is to use multiple Monadic (or in this case Applicative) values for a function. 我最常见的用途是为函数使用多个Monadic(或在本例中为Applicative)值。 A really common way I use it is for constructors. 我使用它的一种非常常见的方式是构造函数。

Consider: 考虑:

randomAge :: Rand StdGen Int
randomHeight :: Rand StdGen Double
randomWeight :: Rand StdGen Double

data Person = Person { age :: Int, height :: Double, weight :: Double }

randomPerson :: Rand StdGen Person
randomPerson = Person <$> randomAge <*> randomHeight <*> randomWeight

-- If we only had Monads...
randomPerson' :: Rand StdGen Person
randomPerson' = liftM3 Person randomAge randomHeight randomWeight
-- or worse...
randomPerson'' = randomAge >>= \ra -> randomHeight >>= \rh -> randomWeight >>= \rw -> return $ Person ra rh rw

Or this example from FPComplete's tutorial on the JSON parser Aeson : 或者来自FPComplete的JSON解析器Aeson教程的这个例子:

instance FromJSON Person where
 parseJSON (Object v) =
    Person <$> v .: "firstName"
           <*> v .: "lastName"
           <*> v .: "age"
           <*> v .: "likesPizza"
 parseJSON _ = mzero

Or this example from the Real World Haskell chapter on Parser Combinators : 或者来自Parser CombinatorsReal World Haskell章节中的这个例子:

ghci> let parser = (++) <$> string "HT" <*> (string "TP" <|> string "ML")

For the most part Applicatives are useful when you have f :: a -> b -> c and you have ma and mb and you want an mc . 大多数情况下,如果你有f :: a -> b -> c并且你有mamb并且你想要一个mc ,那么Applicatives很有用。

Edit: That said there are other uses. 编辑:那说还有其他用途。 For example, Applicative can be used as a really good way to modify traversable data structures. 例如,Applicative可以用作修改可遍历数据结构的非常好的方法。 Eg applying a tree to a value, by having a tree of functions, thereby turning a value into a tree. 例如,通过具有函数树将树应用于值,从而将值转换为树。 Or doing non-deterministic list operations without list comprehension syntax. 或者在没有列表理解语法的情况下进行非确定性列表操作。

It can have some unexpected applications. 它可能有一些意想不到的应用。 For example, using the -> Monad: 例如,使用-> Monad:

> (<*>) (+) (+1) 2
5

That last one was a bit more esoteric and isn't likely to be used in a practical circumstance but it shows that you can use Applicatives in many ways. 最后一个有点深奥,不太可能在实际环境中使用,但它表明你可以在很多方面使用Applicatives。

It's the basic building block (along with pure ) for other very useful functions like liftA2 (which you could think of as " fmap over two containers" and not be too far off the mark): 它是其他非常有用的功能的基本构建块(以及pure ),例如liftA2 (您可以将其视为“两个容器上的fmap ”并且距离标记不太远):

liftA2 :: Applicative f => (a -> b -> c) -> f a -> f b -> f c
liftA2 f fa fb = pure f <*> fa <*> fb

So suppose you want to add all combinations of values of two lists xs, ys :: [Integer] , you could do it this way: 因此,假设您要添加两个列表xs, ys :: [Integer]的值的所有组合,您可以这样做:

liftA2 (+) xs ys

Though more commonly we write this: 虽然我们更常见的是写这个:

(+) <$> xs <*> ys

You are right to be puzzled why would somebody ever write [(+),(*)] <*> [1,2] <*> [3,4] —I think it's a very artificial example. 你感到困惑的是为什么有人会写[(+),(*)] <*> [1,2] <*> [3,4] - 我认为这是一个非常人为的例子。 99% of the time, Applicative is used, you fundamentally have one function that takes two or more arguments, and are looking to apply it to the values inside a functor that implements Applicative . 99%的时候,使用Applicative ,你基本上有一个函数需要两个或多个参数,并且希望将它应用于实现Applicative的仿函数中的值。

One way of looking at it is that we could alternatively define Applicative and associated functions like this: 查看它的一种方法是我们可以选择定义Applicative和相关函数,如下所示:

-- Not the real definition
class Functor f => Applicative f where
    pure :: a -> f a

    -- Like `fmap` but for two-argument functions:
    liftA2 :: (a -> b -> c) -> f a -> f b -> f c

-- `<*>` and `liftA2` are interdefinable, as shown further up and just here:
(<*>) :: Applicative f => f (a -> b) -> f a -> f b
ff <*> fa = liftA2 ($) ff fa

But the reason <*> is picked as the class method is that it makes it very easy to write liftA2 , liftA3 , etc.: 但选择<*>作为类方法的原因是它使编写liftA2liftA3等变得非常容易:

liftA2 :: Applicative f => (a -> b -> c) -> f a -> f b -> f c
liftA2 f fa fb = pure f <*> fa <*> fb

liftA3 :: Applicative f => (a -> b -> c -> d) -> f a -> f b -> f c -> f d
liftA3 f fa fb fc = pure f <*> fa <*> fb <*> fc

liftA4 :: Applicative f => (a -> b -> c -> d -> e) -> f a -> f b -> f c -> f d -> f e
liftA4 f fa fb fc fd = pure f <*> fa <*> fb <*> fc <*> fd

The Applicative class is very nearly the minimum functionality needed to support this "map with a function of multiple arguments" pattern, which is a useful thing in tons of cases. Applicative类几乎是支持这种“具有多个参数的函数的映射”模式所需的最小功能,这在很多情况下是有用的。

As leftaroundabout mentioned, whenever the "shape" of each "action" doesn't depend on the values "produced" by the previous one, Applicative is sufficient. 如左下所述,每当“动作”的“形状”不依赖于前一个“产生”的值时, Applicative就足够了。

This may be a shape in a very literal way. 这可能是一种非常直观的形状。 For example, one context for >>= is that t >>= f takes values stored in the leaves of a tree t , applies f to each value to produce a new subtree, and grafts each subtree onto the tree in place of its corresponding leaf. 例如, >>=一个上下文是t >>= f获取存储在树t的叶子中的t ,将f应用于每个值以生成新子树,并将每个子树移植到树上以代替其对应的叶。 This operation, however, doesn't work for a balanced tree, because there's no telling how large/deep a tree f will produce each time. 但是,此操作不适用于平衡树,因为无法知道每次树f将产生多大/深。 However, <*> may still make sense, because the grafted trees will all have the same shape, maintaining balance. 然而, <*>可能仍然有意义,因为嫁接树将具有相同的形状,保持平衡。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM