Haskell应用变形金刚的例子

use*_*560 11 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"]
Run Code Online (Sandbox Code Playgroud)

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

错误在最后给出.

那么,我如何将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>
Run Code Online (Sandbox Code Playgroud)

C. *_*ann 12

请考虑以下类型签名:

liftA2 :: (Applicative f) => (a -> b -> c) -> f a -> f b -> f c
(<*>) :: (Applicative f) => f (a -> b) -> f a -> f b
Run Code Online (Sandbox Code Playgroud)

合并后,结果类型为:

liftA2 (<*>) :: (Applicative f, Applicative g) 
             => f (g (a -> b)) -> f (g a) -> f (g b)
Run Code Online (Sandbox Code Playgroud)

这确实是两个人Applicative的组合.事实上,它恰好是两个组合Applicative.换句话说,虽然您可以Applicative以通用方式组合s,但这不会以任何方式自动完成.一切都必须明确提升正确的次数.

你的ex功能相当于liftA2 (:),它有类型(Applicative f) => f a -> f [a] -> f [a].通过您的示例,对您想要做的事情进行一些猜测:

test1 = ex "abc" ["pqr", "xyz"]
Run Code Online (Sandbox Code Playgroud)

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

test2 = ex "abc" [Just "pqr", Just "xyz"]
Run Code Online (Sandbox Code Playgroud)

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

test3 = ex "abc" (Just "pqr")
Run Code Online (Sandbox Code Playgroud)

这次第二个参数是类型Maybe [Char],所以fMaybe,我们只需要一个提升.因此,第一个参数应该是类型Maybe Char.

test4 = ex (Just 'a') ["pqr", "xyz"]
Run Code Online (Sandbox Code Playgroud)

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

test5 = ex (return ("abc"):: IO ()) [Just "pqr", Just "xyz"]
Run Code Online (Sandbox Code Playgroud)

这里的类型签名毫无意义; 你可能想要的IO [Char].第二个参数有类型[Maybe [Char]].像前面的例子一样,他们不匹配,但这次你有三个 Applicative.如果你想要类似的东西IO [Maybe a],你需要举起(:)三次,例如liftA2 (liftA2 ex).

这种组合方式Applicative称为"仿函数组合",您链接的页面提到定义显式组合类型构造函数的库.例如,使用transformers,您可以使用类似于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 sm.

现在,观察非组合组合StateT s (Either e)本质上是像Parsec这样的库中使用的典型解析器组合monad的简化版本,并且这样的解析器是使用Applicative样式流行的众所周知的地方之一.因此,建议monad变压器式组合在某种程度上是不必要的或多余的,这似乎有点误导Applicative!


npo*_*cop 11

维基文章说,liftA2 (<*>)可以用来撰写应用函子.很容易看到如何从其类型中使用它:

o :: (Applicative f, Applicative f1) =>
     f (f1 (a -> b)) -> f (f1 a) -> f (f1 b)
o = liftA2 (<*>)
Run Code Online (Sandbox Code Playgroud)

所以,如果fMaybef1[]我们得到:

> Just [(+1),(+6)] `o` Just [1, 6] 
Just [2,7,7,12]
Run Code Online (Sandbox Code Playgroud)

另一种方式是:

>  [Just (+1),Just (+6)] `o` [Just 1, Just 6]
[Just 2,Just 7,Just 7,Just 12]
Run Code Online (Sandbox Code Playgroud)

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

test1 = liftA2 (:) "abc" ["pqr", "xyz"]
Run Code Online (Sandbox Code Playgroud)

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

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

但是它只适用于两个操作数同样很深的情况.所以除了双倍liftA2你应该pure用来修复关卡:

*Main> (liftA2 . liftA2) (:) (pure "abc") (Just ["pqr", "xyz"])
Just ["apqr","axyz","bpqr","bxyz","cpqr","cxyz"]
Run Code Online (Sandbox Code Playgroud)


Dan*_*ner 5

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

您仍然需要编写一些代码,可以使用不同的mungers <*>,pure也可以添加一些newtype噪声.例如,使用TypeCompose包,您可以编写

test2 = ex (O (Just "abc")) [O (Just "pqr"), O (Just "xyz")]
Run Code Online (Sandbox Code Playgroud)

  • 来自变换器包的`Data.Functor.Compose`可能是更常用的数据类型.它还有`Data.Functor.Product`,这是组合applicative functor的另一种方式. (2认同)

mac*_*ron 5

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

如果我们a为特定纯值的类型编写x,因此没有副作用,那么我们可以f使用pure组合子将此纯值提升为应用函子的计算.但是,同样的道理,我们可以使用pure函数从gApplicative情况下解除pure xg仿函数.

pure (pure x) :: g (f a)
Run Code Online (Sandbox Code Playgroud)

现在g (f a)是结合了影响g和影响的计算类型f.看看你的测试,我们注意到了

test1 :: [String]
Run Code Online (Sandbox Code Playgroud)

您只使用了一种效果test1,即列表实例Applicative为您提供的非确定性.确实,打破它:

"abc" :: String
((:) <$>) :: [Char] -> [String -> String]
((:) <$> "abc") :: [String -> String]
((:) <$> "abc" <*> ["pqr", "xyz"]) :: [String]
Run Code Online (Sandbox Code Playgroud)

现在,如果我们想要构成失效效应和非确定性效应,我们期望构建一个类型的计算Maybe [a],或者也许[Maybe a].事实证明,这两者是等价的,因为应用仿函数总是通勤.

这是类型的计算[Maybe Char].它将非确定地返回a Char,但如果确实如此,它可能会失败:

x1 = [Just 'a', Just 'b', Just 'c']
Run Code Online (Sandbox Code Playgroud)

同样,这是一个类型的计算[Maybe String]:

x2 = [Just "pqr", Just "xyz"]
Run Code Online (Sandbox Code Playgroud)

现在我们想要解除(:)这个组合的applicative functor.为此,我们必须将其提升两次:

pure (pure (:)) :: [Maybe (Char -> String -> String)]
Run Code Online (Sandbox Code Playgroud)

同样,要应用它,我们需要通过两个仿函数推动这个计算.因此,我们可以推出一种新的组合(<<*>>)器:

(<<*>>) :: (Applicative f, Applicative f1) =>
           f (f1 (a -> b)) -> f (f1 a) -> f (f1 b)
(<<*>>) = liftA2 (<*>)
Run Code Online (Sandbox Code Playgroud)

现在允许我们写:

pure (pure (:)) <<*>> x1 <<*>> x2
Run Code Online (Sandbox Code Playgroud)

你可以检查具有预期的类型.

但是,由于applicative functors在组合下是封闭的,[Maybe a]因此它本身就是一个应用程序的functor,所以你可能希望能够重用pure(<*>).变形金刚包的Data.Functor.Compose模块向您展示如何操作.