如何测试自定义 StateT 的 Monad 实例?

2024-05-29

我正在学习 Monad Transformers,其中一个练习要求实现 Monad 实例StateT。 我想使用以下方法测试我的实现是否符合 Monad 法则validity https://github.com/NorfairKing/validity包,就像checkers包裹。

问题是,我的Arbitrary实例无法编译。我看见这个问题 https://stackoverflow.com/a/43415006/839733,但它并没有完全达到我想要的效果,因为测试基本上重复了实现并且不检查法律。 还有这个问题 https://stackoverflow.com/q/38930234/839733,但还没有答案,而且我已经想出了如何测试不涉及函数的 Monad Transformers(比如MaybeT).

{-# LANGUAGE DerivingVia #-}
{-# LANGUAGE InstanceSigs #-}

module Ch11.MonadT (StT (..)) where

import Control.Monad.Trans.State (StateT (..))

newtype StT s m a = StT (s -> m (a, s))
  deriving
    (Functor, Applicative)
    via StateT s m

instance (Monad m) => Monad (StT s m) where
  return :: a -> StT s m a
  return = pure

  (>>=) :: StT s m a -> (a -> StT s m b) -> StT s m b
  StT x >>= f = StT $ \s -> do
    (k, s') <- x s
    let StT y = f k
    y s'

  (>>) :: StT s m a -> StT s m b -> StT s m b
  (>>) = (*>)

My test:

{-# LANGUAGE DerivingVia #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE TypeApplications #-}

module Ch11.MonadTSpec (spec) where

import Ch11.MonadT (StT (..))
import Test.Hspec
import Test.QuickCheck
import Test.Validity.Monad

spec :: Spec
spec = do
  monadSpecOnArbitrary @(StTArbit Int [] Int)

-- create wrapper to avoid orphan instance error
newtype StTArbit s m a = StTArbit (StT s m a)
  deriving
    (Functor, Applicative, Monad)

instance (Arbitrary s, Function s, Arbitrary1 m, Arbitrary a) => Arbitrary (StTArbit s m a) where
  arbitrary = do
    f <- arbitrary :: Fun s (m (a, s))
    StTArbit . StT <$> f

Error:

• Couldn't match type: (a0, s0)
                 with: s -> m (a, s)
  Expected: Gen (s -> m (a, s))
    Actual: Gen (a0, s0)
• In the second argument of ‘(<$>)’, namely ‘f’
  In a stmt of a 'do' block: StTArbit . StT <$> f

OP在这里,这就是我最终所做的。

-- https://ghc.gitlab.haskell.org/ghc/doc/users_guide/exts/explicit_forall.html
{-# LANGUAGE ExplicitForAll #-}
-- https://ghc.gitlab.haskell.org/ghc/doc/users_guide/exts/type_applications.html
{-# LANGUAGE TypeApplications #-}

module Ch11.MonadTSpec (spec) where

import Ch11.MonadT (StT (..), runStT)
import Data.Function as F
import Test.Hspec
import Test.Hspec.QuickCheck
import Test.QuickCheck

spec :: Spec
spec = do
  describe "Monad (StT Int [])" $ do
    describe "satisfies Monad laws" $ do
      -- the types are in the same order as in `forall`
      prop "right identity law" (prop_monadRightId @Int @Int @[])
      prop "left identity law" (prop_monadLeftId @Int @Int @Int @[])
      prop "associative law" (prop_monadAssoc @Int @Int @Int @Int @[])

{- HLINT ignore -}

{-
the types in `forall` are specified in the order of dependency.
since `m` needs `a` and `s`, those appear before `m` in the list.
-}

-- (x >>= return) == x
prop_monadRightId ::
  forall a s m.
  (Monad m, Eq (m (a, s)), Show (m (a, s))) =>
  s ->
  Fun s (m (a, s)) ->
  Property
prop_monadRightId s f = ((===) `F.on` go) (m >>= return) m
  where
    m = StT $ applyFun f
    go st = runStT st s

-- (return x >>= f) == (f x)
prop_monadLeftId ::
  forall a b s m.
  (Monad m, Eq (m (b, s)), Show (m (b, s))) =>
  a ->
  s ->
  Fun (a, s) (m (b, s)) ->
  Property
prop_monadLeftId a s f = ((===) `F.on` go) (return a >>= h) m
  where
    g = applyFun2 f
    m = StT $ g a
    h = StT . g
    go st = runStT st s

-- ((x >>= f) >>= g) == (x >>= (\x' -> f x' >>= g))
prop_monadAssoc ::
  forall a b c s m.
  (Monad m, Eq (m (b, s)), Show (m (b, s)), Eq (m (c, s)), Show (m (c, s))) =>
  s ->
  Fun s (m (a, s)) ->
  Fun (a, s) (m (b, s)) ->
  Fun (b, s) (m (c, s)) ->
  Property
prop_monadAssoc s h f g =
  ((===) `F.on` go)
    ((m >>= f') >>= g')
    (m >>= (\x -> f' x >>= g'))
  where
    m = StT $ applyFun h
    f' = StT . applyFun2 f
    g' = StT . applyFun2 g
    go st = runStT st s
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何测试自定义 StateT 的 Monad 实例? 的相关文章

  • 地图不是接受一个函数而列表返回一个列表吗?

    map2 List a gt b gt c gt a gt b gt c map2 List f map2 List f a as bs map f a bs map2 List f as bs 这是我的讲座中的一个示例 它尝试将二元函数应
  • 为什么解析器组合器“seq”用“bind”和“return”定义?

    我正在读这个article http eprints nottingham ac uk 237 1 monparsing pdf关于解析器组合器并且不理解以下内容 他们说使用seq 见下文 导致解析器将嵌套元组作为结果 操作起来很混乱 se
  • 如何避免编写这种类型的 Haskell 样板代码

    我经常遇到这种情况 这很烦人 假设我有一个 sum 类型 它可以保存一个实例x或一堆其他无关的事情x data Foo x X x Y Int Z String other constructors not involving x 要声明
  • 类型级编程有哪些示例? [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 我不明白 类型级编程 是什么意思 也无法使用Google找到合适的解释 有人可以提供一个演示类型级编程的示例吗 范式的解释和 或定义将
  • 如何使用 Haskell 提交 html 表单

    我知道如何使用http 管道 http hackage haskell org package http conduit 2 1 0包的 simplehttp 从 URL 检索页面 现在如果那样的话怎么办 网页有一个输入文本字段和一个提交按
  • 为什么我不能将 Int 类型与 a 类型匹配

    哈斯克尔新手在这里 我在这里尝试做的事情的一个过于简单的例子 test Int gt a test i i Couldn t match expected type a with actual type Int a is a rigid t
  • Repa 数组上的并行 mapM

    在我最近的work https github com bgamari mixture model with Gibbs sampling 我一直在充分利用RVar http hackage haskell org packages arch
  • Haskell 如何将整数文字转换为不同类型?

    我有以下匿名函数 Exercises gt g Sum n gt Sum n 1 我这样使用它 Exercises gt g Sum 56 Sum getSum 55 Exercises gt g 56 Sum getSum 55 第二个例
  • enumFromTo 如何工作?

    我无法将号码添加到Char 以下内容将无法编译 a 1 但是 a z 成功创建一个字符串 其中每个字符值都会递增 有没有一个特殊的函数可以增加Char 我知道我能做到chr ord c 1 如何 a z 或底层enumFromTo函数增加结
  • 为连续可测量的现象创建行为

    我想创建一个Behavior t a从一个IO a 其预期语义是每次行为发生时都会运行 IO 操作sampled language FlexibleContexts import Reflex Dom import Control Mona
  • 为什么 Kleisli 不是 Monoid 的一个实例?

    如果您希望附加两个类型 a gt m b 的函数 以便只得到一个附加两个结果的相同类型的函数 您可以使用 Kleisli 来执行此操作 instance Monad m Monoid b gt Monoid Kleisli m a b wh
  • 使用 Parsec 解析数据并省略注释

    我正在尝试编写一个 Haksell Parsec 解析器 它将文件中的输入数据解析为 LogLine 数据类型 如下所示 Final parser that holds the indvidual parsers final Parser
  • 优化计算 200 万以下所有素数总和的 Haskell 代码

    欧拉计划中的问题 10 我在那里看到了一些讨论 但仅限于 C 我用下面的代码来计算 print sum sieve 2 2000000 where sieve sieve x xs x sieve filter 0 mod x xs 需要很
  • Haskell 中的随机枢轴快速排序

    是否有可能在 Haskell 中实现快速排序 使用 RANDOM PIVOT 但仍然有一个简单的Ord a gt a gt a 签名 我开始了解 Monad 目前 我将 monad 解释为某种 命令模式 这对于 IO 非常有用 所以 我知道
  • 关注点分离:什么时候最好将语义与语法分离?

    Choices 类型类的出色之处在于它们允许我们将额外的结构连接到现有类型 从而使我们能够推迟一些设计决策 而不是在构思时匆忙做出决定 另一方面 例如 在面向对象编程中 我们被迫考虑类型需要立即执行什么操作 以及稍后出现的或需要的任何附加结
  • 数据记录的类约束

    我有一个data type data BuildException a KillBuild JobID a Stage FailBuild JobID a Stage CancelBuild JobID a Stage StopBuild
  • 如何编写将布尔值返回到一个函数的函数

    我在这里发现了一个类似的问题 它问了几乎相同的问题 但又不完全一样 我的问题是如何将 a gt Bool 类型的函数列表组合成一个也是 a gt Bool 的函数 Ex compose a gt Bool gt a gt Bool comp
  • 当您包含导入 Gloss 的项目时,“stack ghci”会失败

    如果您在 Stack 项目中导入 Gloss 并使用stack ghci 您会收到以下错误 GHCi version 7 10 2 http www haskell org ghc for help
  • 如何在Go包之间共享测试接口?

    Go 不会在不同包的测试文件之间共享代码 因此测试接口的定义不会自动重用 在实践中我们如何解决这个问题 使用示例testing quick foo foo go package foo type Thing int const X Thin
  • Java 中更高级的泛型

    假设我有以下课程 public class FixExpr Expr

随机推荐