Haskell quickBatch:Ap适用的半身像 [英] Haskell quickBatch: Ap applicative monoid

查看:50
本文介绍了Haskell quickBatch:Ap适用的半身像的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

根据 ZipList Monoid haskell 中提供的建议,我创建了以下有效的代码:

  newtype Ap f a = Ap {getAp :: f a}推导(Eq,Show)实例(适用f,半群a)=>半群(Ap f a)其中Ap xs<>Ap ys =Ap $ liftA2(<)xs ys实例(适用f,Monoid a)=>Monoid(Ap f a)其中虚空= Ap $纯虚空Ap xs`mappend` Ap ys =Ap $ liftA2 mappend xs ys应用程式:: Ap ZipList(Sum Int)app = Ap(ZipList [1,2:Sum Int])instance任意(f a)=>任意(Ap f a)其中任意= Ap< $>随意的实例Eq a =>EqProp(Ap ZipList a)其中xs =-= ys = xs'eq` ys'其中xs'=令(Ap(ZipList l))= xs摄取3000公升ys'=令l =(getZipList.getAp)ys摄取3000公升main :: IO()主=做quickBatch $ monoid应用 

但是,我不完全了解代码的工作方式.为什么 mempty = Ap $ pure mempty ?该方程如何计算或推导?为什么是 Ap xs'mappend'Ap ys ?我会以为是 Monoid(Ap f a),所以应该是 Ap f xs'mappend'Ap f ys ?

为什么在ap上运行quickBatch monoid测试时,却不会导致mconcat测试中的stackoverflow,如解决方案

为什么它是 mempty = Ap $ pure mempty ?该方程如何计算或推导?为什么是 Ap xs'mappend'Ap ys ?我会以为是 Monoid(Ap f a),所以应该是 Ap f xs'mappend'Ap f ys ?

为了回答这些问题,重要的是仔细查看 Ap 本身的定义:

  newtype Ap f a = Ap {getAp :: f a} 

此声明同时引入了新的类型 Ap 和新的构造函数 Ap ,它们具有相同的名称,但它们绝对是不同的实体.

类型 Ap 具有类型(Type-> Type)->类型->类型,也就是说,它带有两个参数: f 本身是类型的函数,而 a 只是类型.当我们编写诸如 app :: Ap ZipList(Sum Int)之类的东西或诸如 instance Eq a =>EqProp(Ap ZipList a).

构造函数 Ap 的类型为 f a->.Ap f a (请在此处注意使用 Ap 的类型版本!).此构造函数采用一个 one 参数(一个值为 f a 的值),以产生一个类型为 Ap f a 的值.因此,例如,您可以编写:

  t1 :: Ap Maybe Intt1 = Ap(仅3月)t2 :: Ap []布尔t2 = Ap [True,False]t3 :: Ap ZipList Intt3 = Ap(ZipList [1,2,3]) 

请注意,在每种情况下, type Ap 带有两个参数,而构造函数 Ap 带有一个参数.

现在,让我们考虑如何为 Ap f a 编写 Monoid 实例.让我们回想一下 Monoid 类:

  class Semigroup a =>Monoid在哪里暴躁:: amappend :: a->a->一种 

因此,对于实例(适用f,Monoid a)=>Monoid(Ap f a),我们需要 mempty :: Ap fa mappend :: Ap f a->Ap f a->Ap fa .我们怎么写 mempty ?好吧,首先,我们需要一个类型为 Ap f a 的值,到目前为止,到目前为止,制作方法的唯一方法是使用 Ap 构造函数.调用 Ap 构造函数,这意味着我们需要一个类型为 f a 的值,我们可以将其提供给它.我们如何生产其中之一?幸运的是,我们知道 Monoid a ,因此我们可以访问 mempty :: a ,并且我们知道 Applicative f ,因此我们可以访问 pure :: forall x.x->f x .将这两个放在一起,我们可以得到一个 pure mempty :: f 的值.剩下的就是提供给 Ap 构造函数:

  mempty = Ap $ pure mempty 

接下来,我们需要定义 mappend .我们给了两个类型为 Ap fa 的值,这意味着对于某些值 x ,它们必须为 Ap x 的形式(请记住, Ap x 中的 Ap 是构造函数,而不是类型.因此,我们从模式匹配开始:

 <代码> Ap xs`mappend` Ap ys = ... 

xs ys 有哪些类型?好吧, Ap xs :: Ap fa Ap :: f a->Ap f a ,所以 xs,ys :: f a .我们需要以某种方式将 fa 类型的这两个值组合为 fa 类型的单个值,然后可以使用 Ap 构造函数包装将其输出.我们可以使用 liftA2 mappend xs ys 来做到这一点.这给了我们:

 <代码> Ap xs`mappend` Ap ys = Ap $ liftA2 mappend xs ys 

作为此处的注释,请查看写这样的东西怎么没有意义:

 <代码> Ap f xs`mappend` Ap g ys` =-模式错误! 

因为我们要混合使用两个参数的 Ap 类型和仅使用一个参数的构造函数 Ap .


为什么为什么在Ap上运行quickBatch monoid测试时,却不会在mconcat测试中导致stackoverflow

堆栈溢出是尝试比较两个无限列表是否相等的结果.GHC只会继续检查每个元素,以寻找列表的结尾还是两个不相等的元素,并且由于列表无限长,因此程序不会终止(或耗尽内存).

但是,在您对 Ap ZipList a EqProp 的定义中,您基本上是在说,仅检查列表的前3000个元素是否相等是可以的.因此,即使遇到无限列表,只要前3000个元素相等,我们就可以继续假设这些列表相等.

Based on the suggestion provided at ZipList Monoid haskell, I have created this code which works:

newtype Ap f a = Ap { getAp :: f a }
  deriving (Eq, Show)
instance (Applicative f, Semigroup a) =>
  Semigroup (Ap f a) where
    Ap xs <> Ap ys = 
      Ap $ liftA2 (<>) xs ys
instance (Applicative f, Monoid a) => 
  Monoid (Ap f a) where
    mempty = Ap $ pure mempty
    Ap xs `mappend` Ap ys = 
      Ap $ liftA2 mappend xs ys
app :: Ap ZipList (Sum Int)
app = Ap (ZipList [1,2 :: Sum Int])
instance Arbitrary (f a) =>
  Arbitrary (Ap f a) where
    arbitrary = Ap <$> arbitrary 
instance Eq a => EqProp (Ap ZipList a) where
  xs =-= ys = xs' `eq` ys' where 
    xs' = 
      let (Ap (ZipList l)) = xs
        in take 3000 l
    ys' = 
      let l = (getZipList . getAp) ys
        in take 3000 l
main :: IO ()
main = do
  quickBatch $ monoid app

However, I do not fully understand how the code works. Why is it mempty = Ap $ pure mempty? How is this equation calculated or derived? Why is it Ap xs 'mappend' Ap ys? I would have thought that since it is Monoid (Ap f a), it should be Ap f xs 'mappend' Ap f ys?

And why is it that when the quickBatch monoid tests are run on Ap, it doesn't result in stackoverflow at the mconcat test as seen at Haskell quickBatch: Testing ZipList Monoid at mconcat results in stack overflow?

解决方案

Why is it mempty = Ap $ pure mempty? How is this equation calculated or derived? Why is it Ap xs 'mappend' Ap ys? I would have thought that since it is Monoid (Ap f a), it should be Ap f xs 'mappend' Ap f ys?

In order to answer these questions, it will be important to take a careful look at the definition of Ap itself:

newtype Ap f a = Ap { getAp :: f a }

This declaration introduces both a new type Ap and a new constructor Ap—they have the same name but they are definitely different entities.

The type Ap has kind (Type -> Type) -> Type -> Type, which is to say that it takes two arguments: f, which itself is a function on types, and a, which is just a type. We can use the type Ap in type signatures, when we write things like app :: Ap ZipList (Sum Int) or in class instances like instance Eq a => EqProp (Ap ZipList a).

The constructor Ap has type f a -> Ap f a (note the use of the type version of Ap here!). This constructor takes one argument, a value of type f a, in order to produce a value of type Ap f a. So, for instance, you can write:

t1 :: Ap Maybe Int
t1 = Ap (Just 3)

t2 :: Ap [] Bool
t2 = Ap [True, False]

t3 :: Ap ZipList Int
t3 = Ap (ZipList [1,2,3])

Notice that in every case, the type Ap takes two arguments, but the constructor Ap takes one argument.

Now, let's consider how to write the Monoid instance for Ap f a. Let's recall the Monoid class:

class Semigroup a => Monoid a where
  mempty  :: a
  mappend :: a -> a -> a

So, for instance (Applicative f, Monoid a) => Monoid (Ap f a), we'll need mempty :: Ap f a and mappend :: Ap f a -> Ap f a -> Ap f a. How can we write mempty? Well, first off, we need a value of type Ap f a, and the only way we've seen so far for how to make one is with the Ap constructor. Recalling the Ap constructor, that means we need a value of type f a that we can feed to it. How can we produce one of those? Fortunately, we know Monoid a, so we have access to mempty :: a, and we know Applicative f, so we have access to pure :: forall x. x -> f x. Sticking these two together, we can make a value pure mempty :: f a. All that's left is to provide that to the Ap constructor:

  mempty = Ap $ pure mempty

Next, we need to define mappend. We're given two values of type Ap f a, which means they must be of the form Ap x for some values x (remember that the Ap in Ap x here is the constructor, not the type). So, we begin by pattern matching:

  Ap xs `mappend` Ap ys = ...

What types do xs and ys have? Well, Ap xs :: Ap f a, and Ap :: f a -> Ap f a, so xs, ys :: f a. We need to somehow combine these two values of type f a into a single value of type f a, and then we can use the Ap constructor to wrap it up for output. We can do this with liftA2 mappend xs ys. This gives us:

  Ap xs `mappend` Ap ys = Ap $ liftA2 mappend xs ys

As a note here, check out how it wouldn't make sense to write something like:

  Ap f xs `mappend` Ap g ys` = -- pattern error!

because we'd be mixing up the type Ap, which takes two arguments, with the constructor Ap, which takes only one.


And why is it that when the quickBatch monoid tests are run on Ap, it doesn't result in stackoverflow at the mconcat test

The stack overflow is the result of trying to compare two infinite lists for equality. GHC will just keep checking each element looking for either the end of the list or for two elements that aren't equal, and since the lists are infinitely long, the program won't terminate (or will run out of memory).

However, in your definition of EqProp for Ap ZipList a, you're basically saying that it's okay to only check the first 3000 elements of a list for equality. So, even if infinite lists are encountered, as long as the first 3000 elements are equal, we can just go ahead and assume that the lists are equal.

这篇关于Haskell quickBatch:Ap适用的半身像的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆