简体   繁体   English

Haskell quickBatch:Ap applicative monoid

[英]Haskell quickBatch: Ap applicative monoid

Based on the suggestion provided at ZipList Monoid haskell , I have created this code which works:根据ZipList Monoid haskell提供的建议,我创建了这个有效的代码:

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 ?为什么它是mempty = Ap $ pure mempty How is this equation calculated or derived?这个方程是如何计算或推导出来的? Why is it Ap xs 'mappend' Ap ys ?为什么是Ap xs 'mappend' Ap ys I would have thought that since it is Monoid (Ap fa) , it should be Ap f xs 'mappend' Ap f ys ?我本来以为既然是Monoid (Ap fa) ,它应该是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 ?为什么在 Ap 上运行 quickBatch monoid 测试时,它不会导致 mconcat 测试中的堆栈溢出,如Haskell quickBatch 所示:在 mconcat 测试 ZipList Monoid 导致堆栈溢出

It is in fact the only possible definition (except ⊥).它实际上是唯一可能的定义(⊥ 除外)。

  • Knowing nothing more about the monoid, the only expressions you can write are of the form mempty <> mempty <>... <> mempty , which is just mempty by monoid laws.对幺半群一无所知,您可以编写的唯一表达式是mempty <> mempty <>... <> mempty ,根据幺半群定律,这只是mempty
  • Knowing nothing more about the applicative, only expressions you can write are of the form f <$> pure y <*> pure z <*>... , which is the same as pure $ fy z... where f is some n-ary function on the monoid – which again can only have the form \y' z'... -> y' <> mempty <>... <> z' <> mempty , and also y and z can only be mempty .对应用程序一无所知,只有您可以编写的表达式的形式f <$> pure y <*> pure z <*>... ,这与pure $ fy z...相同,其中f是一些幺半群上的 n 元 function - 再次只能具有\y' z'... -> y' <> mempty <>... <> z' <> mempty ,并且yz也只能mempty

So, whatever expression you could possibly write, it'll always be identical to pure mempty , provided the monoid and applicative you put in are law-abiding.所以,无论你能写什么表达式,只要你输入的 monoid 和 applicative 是合规的,它总是与pure mempty相同。

Why is it mempty = Ap $ pure mempty ?为什么它是mempty = Ap $ pure mempty How is this equation calculated or derived?这个方程是如何计算或推导出来的? Why is it Ap xs 'mappend' Ap ys ?为什么是Ap xs 'mappend' Ap ys I would have thought that since it is Monoid (Ap fa) , it should be Ap f xs 'mappend' Ap f ys ?我本来以为既然是Monoid (Ap fa) ,它应该是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:为了回答这些问题,仔细看看Ap本身的定义是很重要的:

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.这个声明引入了一个新的类型Ap和一个新的构造函数Ap它们具有相同的名称,但它们绝对是不同的实体。

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. Ap类型有 kind (Type -> Type) -> Type -> Type ,也就是说它需要两个 arguments: f ,它本身是一个 function 类型,和a ,它只是一个类型。 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) .我们可以在类型签名中使用Ap类型,当我们编写app:: Ap ZipList (Sum Int)或 class 实例时,如instance Eq a => EqProp (Ap ZipList a)

The constructor Ap has type fa -> Ap fa (note the use of the type version of Ap here.).构造函数Ap的类型为fa -> Ap fa (注意此处使用的是Ap的类型版本。)。 This constructor takes one argument, a value of type fa , in order to produce a value of type Ap fa .此构造函数接受一个参数,即fa类型的值,以生成Ap fa类型的值。 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.请注意,在每种情况下,类型Ap采用两个 arguments,但构造函数Ap采用一个参数。

Now, let's consider how to write the Monoid instance for Ap fa .现在,让我们考虑如何为Ap fa编写Monoid实例。 Let's recall the Monoid class:让我们回顾一下Monoid class:

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

So, for instance (Applicative f, Monoid a) => Monoid (Ap fa) , we'll need mempty:: Ap fa and mappend:: Ap fa -> Ap fa -> Ap fa .因此, instance (Applicative f, Monoid a) => Monoid (Ap fa) ,我们需要mempty:: Ap famappend:: Ap fa -> Ap fa -> Ap fa How can we write mempty ?我们怎么写mempty Well, first off, we need a value of type Ap fa , and the only way we've seen so far for how to make one is with the Ap constructor.好吧,首先,我们需要一个Ap fa类型的值,到目前为止,我们看到的唯一方法是使用Ap构造函数。 Recalling the Ap constructor, that means we need a value of type fa that we can feed to it.回想一下Ap构造函数,这意味着我们需要一个fa类型的值,我们可以将其提供给它。 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 -> fx幸运的是,我们知道Monoid a ,所以我们可以访问mempty:: a ,并且我们知道Applicative f ,所以我们可以访问pure:: forall x. x -> fx pure:: forall x. x -> fx . pure:: forall x. x -> fx Sticking these two together, we can make a value pure mempty:: fa .将这两者结合在一起,我们可以创建一个pure mempty:: fa All that's left is to provide that to the Ap constructor:剩下的就是将其提供给Ap构造函数:

  mempty = Ap $ pure mempty

Next, we need to define mappend .接下来,我们需要定义mappend We're given two values of type Ap fa , 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).我们得到了两个Ap fa类型的值,这意味着对于某些值x ,它们必须是Ap x的形式(请记住,此处Ap Ap x中的 Ap 是构造函数,而不是类型)。 So, we begin by pattern matching:所以,我们从模式匹配开始:

  Ap xs `mappend` Ap ys = ...

What types do xs and ys have? xsys有哪些类型? Well, Ap xs:: Ap fa , and Ap:: fa -> Ap fa , so xs, ys:: fa .好吧, Ap xs:: Ap faAp:: fa -> Ap fa ,所以xs, ys:: fa We need to somehow combine these two values of type fa into a single value of type fa , and then we can use the Ap constructor to wrap it up for output.我们需要以某种方式将这两个fa fa的值,然后我们可以使用Ap构造函数将其包装为 output。 We can do this with liftA2 mappend xs ys .我们可以使用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.因为我们会混淆类型Ap ,它需要两个 arguments 和构造函数Ap ,它只需要一个。


And why is it that when the quickBatch monoid tests are run on Ap, it doesn't result in stackoverflow at the mconcat test以及为什么在Ap上运行quickBatch monoid测试时,在mconcat测试时不会导致stackoverflow

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). GHC 将继续检查每个元素以查找列表的末尾或两个不相等的元素,并且由于列表无限长,因此程序不会终止(或将耗尽内存)。

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.但是,在您对Ap ZipList aEqProp的定义中,您基本上是在说只检查列表的前 3000 个元素是否相等是可以的。 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.因此,即使遇到无限列表,只要前 3000 个元素相等,我们就可以仅向前 go 并假设列表相等。

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

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