【问题标题】:Why does smallCheck's `Series` class have two types in the constructor?为什么smallCheck的`Series`类在构造函数中有两种类型?
【发布时间】:2013-05-15 05:36:26
【问题描述】:

这个问题与我的other question有关smallCheckTest.SmallCheck.Series类有关。当我尝试以以下自然方式定义类 Serial 的实例时(@tel 对上述问题的回答向我建议),我得到编译器错误:

data Person = SnowWhite | Dwarf Int
instance Serial Person where ...

原来Serial 想要有两个参数。反过来,这需要一些编译器标志。以下作品:

{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses #-}
import Test.SmallCheck
import Test.SmallCheck.Series
import Control.Monad.Identity

data Person = SnowWhite | Dwarf Int

instance Serial Identity Person where
        series = generate (\d -> SnowWhite : take (d-1) (map Dwarf [1..7]))

我的问题是:

  1. Identity 放在那里是“正确的做法”吗?我的灵感来自 Test.Series.list 函数的类型(当我第一次看到它时我也觉得非常奇怪):

    list :: Depth -> Series Identity a -> [a]
    

    什么是正确的做法?如果我一看到Identity就盲目地输入它,我会好吗?我应该改用Serial m Integer => Serial m Person 之类的东西吗(这需要一些看起来更可怕的编译器标志:至少FlexibleContextsUndecidableInstances)?

  2. 第一个参数(Serial m n 中的 m)是做什么用的?

    谢谢!

【问题讨论】:

    标签: testing haskell automated-tests monads smallcheck


    【解决方案1】:

    我只是smallcheck的用户而不是开发者,但我认为答案是

    1) 不是真的。你应该让它保持多态,你可以在没有上述扩展的情况下做到这一点:

    {-# LANGUAGE FlexibleInstances, MultiParamTypeClasses #-}
    import Test.SmallCheck
    import Test.SmallCheck.Series
    import Control.Monad.Identity
    
    data Person = SnowWhite | Dwarf Int deriving (Show)
    
    instance (Monad m) => Serial m Person where
            series = generate (\d -> SnowWhite : take (d-1) (map Dwarf [1..7]))
    

    2) 系列目前定义为

    newtype Series m a = Series (ReaderT Depth (LogicT m) a)
    

    这意味着 mLogicT 的基本 monad,用于生成系列中的值。例如,写 IO 代替 m 将允许 IO 操作在生成系列时发生。

    在 SmallCheck 中,m 也出现在 Testable 实例声明中,例如 instance (Serial m a, Show a, Testable m b) => Testable m (a->b)。如果您只有Identity 的实例,则这具有具体的效果,即无法使用诸如smallCheck :: Testable IO a => Depth -> a -> IO () 之类的预先存在的驱动程序函数。

    实际上,您可以通过编写自定义驱动程序函数来利用这一事实 交错一些单子效应,如在所述驱动程序内记录生成的值(或一些这样的值)。

    它也可能对我不知道的其他事情有用。

    【讨论】:

    • 非常感谢您的精彩回答和解释!您的示例有效,它确实应该在文档中。不过,我必须承认,我很生气,因为我必须理解 Series (ReaderT Depth (LogicT m) a) 的含义,只是将库用于具体目的。
    • 你不需要关心LogicT来使用smallcheck。
    • 好吧,也许我不需要更多详细信息。但是,正因为如此,如果没有你的帮助,我永远无法猜出我应该做什么才能让我的代码正常工作。
    • 这绝对是文档中的缺陷---SmallCheck 是一个不如 QuickCheck 受欢迎的库---但要快速回答:ReaderT Depth (LogicT m) a 非常类似于Depth -> [a] .唯一的变化是它允许将来自 monad m 的 monadic 效果交错到您的列表中。特别是允许对分支和条件序列生成进行更高级别描述的逻辑效果。我们可以通过设置m ~ Identity 来消除这些影响,然后它们完全相等:Serial Identity a ~ Depth -> [a]
    • 为了强调这一点,这里是generate f = getDepth >>= \d -> msum $ map return $ f d的定义。换句话说,它使用ReaderT monad 获取Depth,将其传递给您的Depth -> [a] 函数,将return 映射到它上面以获得一堆良性的monadic 效果,然后组合结果。
    猜你喜欢
    • 2019-01-03
    • 1970-01-01
    • 2012-02-10
    • 1970-01-01
    • 2012-05-01
    • 2012-02-08
    • 1970-01-01
    • 2015-05-23
    • 1970-01-01
    相关资源
    最近更新 更多