Haskell应用变形金刚的例子

时间:2012-09-25 16:27:06

标签: haskell monad-transformers applicative

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 个答案:

答案 0 :(得分:12)

考虑以下类型签名:

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)

这确实是两个Applicative的组合。实际上,它是完全两个Applicative的组合。换句话说,虽然可以以通用方式组合Applicative,但这并不是以任何方式完成自动。一切都必须明确提升正确的次数。

您的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"]

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

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

这次第二个参数的类型为Maybe [Char],因此fMaybe,我们只需要一次提升。因此,第一个参数应为Maybe Char类型。

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

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

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

这里的类型签名毫无意义;你可能想要IO [Char]。第二个参数的类型为[Maybe [Char]]。与前面的示例一样,它们不匹配,但这次你有三个 Applicative s。如果您需要IO [Maybe a]之类的内容,则需要将(:)全部提升三次,例如liftA2 (liftA2 ex)

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


作为附录,本声明:

  

那么应用变形金刚在哪里?答案是,我们不需要特殊的变换器用于应用仿函数,因为它们可以通用的方式组合。

......有点虚伪。确实,两个Applicative s的组合也是Applicative,但这不是组合Applicative s的唯一方法!

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

现在,观察非组合组合m本质上是Parsec等库中使用的典型解析器组合monad的简化版本,并且此类解析器是使用{{1}的众所周知的地方之一风格很受欢迎。因此,在StateT s (Either e)关注的地方,monad变换器式组合在某种程度上是不必要的或多余的,似乎有点误导!

答案 1 :(得分:11)

wiki文章说liftA2 (<*>)可以用来组成applicative functor。很容易看出如何从其类型中使用它:

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

因此,如果fMaybef1[],我们会得到:

> 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"]

答案 2 :(得分:5)

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

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

test2 = ex (O (Just "abc")) [O (Just "pqr"), O (Just "xyz")]

答案 3 :(得分:5)

像往常一样,在这里关注类型以确定应用函子的构成应该是什么意义是很有用的。

如果我们为特定纯值a的类型写x,因此没有副作用,那么我们可以将这个纯值提升为应用仿函数{{1}的计算使用f组合子。但出于同样的原因,我们可以使用pure的{​​{1}}实例中的pure函数将g提升到Applicative仿函数中。

pure x

现在g是结合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],但如果确实如此,则可能会失败:

[Maybe Char]

同样,这里是Char类型的计算:

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

现在我们想要将[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]transformers包的pure模块会向您展示如何操作。