11

www.haskell.org 上的 wiki 告诉我们以下有关 Applicative Transformers 的信息:

那么应用变压器在哪里呢?答案是,我们不需要针对应用函子的特殊转换器,因为它们可以以通用方式组合。 http://www.haskell.org/haskellwiki/Applicative_functor#Applicative_transfomers

我尝试了以下方法来尝试组合一堆应用函子。但我得到的只是一堆错误。这是代码:

import Control.Applicative
import System.IO

ex x y = (:) <$> x <*> y 
test1 = ex "abc" ["pqr", "xyz"]  -- only this works correctly as expected
test2 = ex "abc" [Just "pqr", Just "xyz"]
test3 = ex "abc" (Just "pqr")
test4 = ex (Just 'a') ["pqr", "xyz"]
test5 = ex (return ("abc"):: IO ()) [Just "pqr", Just "xyz"]

这会产生很多类型错误,虽然我可以部分理解,但我根本无法解决它们。

错误在最后给出。

那么,例如,我如何结合 Maybe Applicative 和 List Applicative 呢?

例如,如何将 State Applicative 和 List Applicative 结合起来?还有其他例子吗,比如说,结合了 Maybe 和 List,Maybe 和 State,最后是可怕的 IO 和 State 应用程序?

谢谢。

GHCi 错误消息如下。

example.hs:6:19:
    Couldn't match expected type `[Char]' with actual type `Maybe a0'
    In the return type of a call of `Just'
    In the expression: Just "pqr"
    In the second argument of `ex', namely `[Just "pqr", Just "xyz"]'

example.hs:7:19:
    Couldn't match expected type `[[Char]]' with actual type `Maybe a0'
    In the return type of a call of `Just'
    In the second argument of `ex', namely `(Just "pqr")'
    In the expression: ex "abc" (Just "pqr")

example.hs:8:23:
    Couldn't match expected type `Maybe' with actual type `[]'
    In the second argument of `ex', namely `["pqr", "xyz"]'
    In the expression: ex (Just 'a') ["pqr", "xyz"]
    In an equation for `test4': test4 = ex (Just 'a') ["pqr", "xyz"]

example.hs:9:21:
    Couldn't match expected type `()' with actual type `[Char]'
    In the first argument of `return', namely `("abc")'
    In the first argument of `ex', namely `(return ("abc") :: IO ())'
    In the expression:
      ex (return ("abc") :: IO ()) [Just "pqr", Just "xyz"]
Failed, modules loaded: none.
Prelude>
4

4 回答 4

13

考虑以下类型签名:

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

结合起来,得到的类型是:

liftA2 (<*>) :: (Applicative f, Applicative g) 
             => f (g (a -> b)) -> f (g a) -> f (g b)

这确实是两个Applicatives的组合。事实上,它恰好是两个Applicatives 的组合。换句话说,尽管您可以以通用方式组合Applicatives,但这绝不是自动完成的。一切都必须明确提升正确的次数。

您的ex功能等效于liftA2 (:),其类型为(Applicative f) => f a -> f [a] -> f [a]。通过您的示例,对您想要做的事情进行一些猜测:

test1 = ex "abc" ["pqr", "xyz"]

f[],我们将其应用于类型[Char]和的参数[[Char]]

test2 = ex "abc" [Just "pqr", Just "xyz"]

第二个参数是 type [Maybe [Char]],所以我们需要提升两次。第一个参数也需要解除,因为它有类型[Char]并且应该是[Maybe Char]

test3 = ex "abc" (Just "pqr")

这次第二个参数是 type ,Maybe [Char]所以我们只需要一次电梯。因此,第一个参数应该是 type 。fMaybeMaybe Char

test4 = ex (Just 'a') ["pqr", "xyz"]

这次第一个参数是Maybe Char但第二个是[[Char]],所以你有两个完全不同Applicative的s;两者都需要被解除,给你一个[Maybe Char]Maybe [Char]

test5 = ex (return ("abc"):: IO ()) [Just "pqr", Just "xyz"]

这里的类型签名没有意义;你可能想要IO [Char]。第二个参数有 type [Maybe [Char]]。和前面的例子一样,它们不匹配,但这次你有3 个 Applicatives。如果你想要类似的东西IO [Maybe a],你需要举起(:)所有 3 次,例如liftA2 (liftA2 ex)

这种组合Applicatives 的方式称为“函子组合”,您链接到的页面提到了定义显式组合类型构造函数的库。例如,使用transformerslibrary,您可以有一个类型Compose IO (Compose [] Maybe)来描述您的第五个示例。Applicative这种组合类型以上述通用方式定义为实例,并应用正确数量的提升操作。缺点是您需要包装和打开所需的newtype图层。


作为附录,此声明:

那么应用变压器在哪里呢?答案是,我们不需要针对应用函子的特殊转换器,因为它们可以以通用方式组合。

……有点假。确实,两个Applicatives 的组合也是Applicative,但这并不是组合Applicatives 的唯一方法!

考虑StateT s m a,它等价于s -> m (s, a),尽管它的定义略有不同。这也可以写成三个仿函数的组合:((->) s)m((,) s),结果Functor实例是正确的,但Applicative实例是完全错误的。如果您只是从State s a = s -> (a, s)相反开始,则无法StateT s m通过组合State sand来定义m

现在,请注意,非组合组合本质上是 Parsec 等库中使用的典型解析器组合器 monad 的简化版本,并且此类解析器是使用样式流行StateT s (Either e)的众所周知的地方之一。Applicative因此,暗示 monad 转换器风格的组合在某种程度上是不必要或多余的,这似乎有点误导Applicative

于 2012-09-25T17:29:54.263 回答
11

wiki 文章说这liftA2 (<*>)可以用来组成应用函子。从它的类型很容易看出如何使用它:

o :: (Applicative f, Applicative f1) =>
     f (f1 (a -> b)) -> f (f1 a) -> f (f1 b)
o = liftA2 (<*>)

所以对于 if fisMaybef1is[]我们得到:

> Just [(+1),(+6)] `o` Just [1, 6] 
Just [2,7,7,12]

另一种方法是:

>  [Just (+1),Just (+6)] `o` [Just 1, Just 6]
[Just 2,Just 7,Just 7,Just 12]

正如@McCann 所说,您的 ex 函数相当于liftA2 (:)

test1 = liftA2 (:) "abc" ["pqr", "xyz"]

要使用(:)更深层次的应用程序堆栈,您需要以下多个应用程序liftA2

*Main> (liftA2 . liftA2) (:) (Just "abc") (Just ["pqr", "xyz"])
Just ["apqr","axyz","bpqr","bxyz","cpqr","cxyz"]

但是,它仅在两个操作数的深度相同时才有效。因此,除了双倍之外liftA2,您还应该使用它pure来修复级别:

*Main> (liftA2 . liftA2) (:) (pure "abc") (Just ["pqr", "xyz"])
Just ["apqr","axyz","bpqr","bxyz","cpqr","cxyz"]
于 2012-09-25T17:25:20.837 回答
5

说它们可以以通用方式组合并不意味着它们可以隐式或不可见或类似的方式组合。=)

您仍然需要编写一些代码,要么使用不同的芒格,要么<*>添加pure一些新类型的噪音。例如,使用TypeCompose包,您可以编写

test2 = ex (O (Just "abc")) [O (Just "pqr"), O (Just "xyz")]
于 2012-09-25T17:29:17.500 回答
5

像往常一样,在这里关注类型以弄清楚应用函子的组成应该意味着什么是有用的。

如果我们a为特定纯值的类型编写x,因此没有副作用,那么我们可以f使用pure组合器将此纯值提升到应用函子的计算中。但是出于同样的原因,我们可以使用purefromgApplicative实例中的函数来提升pure xg函子中。

pure (pure x) :: g (f a)

现在g (f a)是结合 的效果g和 的效果的计算类型f。查看您的测试,我们注意到

test1 :: [String]

您在 中只使用了一种效果test1,那就是列表实例Applicative给您的不确定性。确实,分解它:

"abc" :: String
((:) <$>) :: [Char] -> [String -> String]
((:) <$> "abc") :: [String -> String]
((:) <$> "abc" <*> ["pqr", "xyz"]) :: [String]

现在,如果我们想组合失败效应和非确定性效应,我们希望构造一个类型为 的计算Maybe [a],或者也许是[Maybe a]。事实证明,两者是等价的,因为 applicative functors 总是commute

这是 type 的计算[Maybe Char]。它将不确定地返回 a Char,但如果返回,它可能会失败:

x1 = [Just 'a', Just 'b', Just 'c']

同样,这是 type 的计算[Maybe String]

x2 = [Just "pqr", Just "xyz"]

现在我们要提升(:)到这个组合的应用函子。为此,我们必须将其提升两次:

pure (pure (:)) :: [Maybe (Char -> String -> String)]

同样,要应用它,我们需要通过两个函子推动这个计算。因此,我们可以引入一个新的组合(<<*>>)器来做到这一点:

(<<*>>) :: (Applicative f, Applicative f1) =>
           f (f1 (a -> b)) -> f (f1 a) -> f (f1 b)
(<<*>>) = liftA2 (<*>)

现在允许我们写:

pure (pure (:)) <<*>> x1 <<*>> x2

您可以检查它是否具有预期的类型。

但是由于应用函子在组合下是封闭的,[Maybe a]它本身就是一个应用函子,因此您可能希望能够重用pure(<*>)变压器包的Data.Functor.Compose模块向您展示了如何操作。

于 2012-09-25T17:40:41.060 回答