You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为包装函数的newtype T1编写Hedgehog生成器以验证Functor实例合规性

解决T1类型的Functor实例属性测试生成器问题

你说得没错,要为T1 a生成随机测试值,确实需要借助hedgehog-fn来生成任意的Int -> a函数——毕竟Hedgehog本身不直接支持生成函数类型的随机值,而hedgehog-fn专门用来处理这类函数生成的需求。下面一步步来实现你需要的genT1,并完成属性测试:

1. 准备依赖

首先确保你的项目里添加了这些依赖(以Cabal为例):

build-depends:
  base >= 4.14 && < 5,
  hedgehog >= 1.2,
  hedgehog-classes >= 0.2,
  hedgehog-fn >= 0.7

2. 编写genT1生成器

核心思路是:用hedgehog-fn的fn函数生成Int -> a类型的随机函数,再把它包装成T1构造器。完整代码示例如下:

{-# LANGUAGE TypeApplications #-}

import Hedgehog
import Hedgehog.Classes
import Hedgehog.Fn (fn)

newtype T1 a = T1 (Int -> a)

-- 你的第一个Functor实例(正确实现)
instance Functor T1 where
  fmap f (T1 a) = T1 (f . a)

-- 你的第二个Functor实例(和第一个完全等价,因为函数的fmap就是组合)
-- instance Functor T1 where
--   fmap f (T1 a) = T1 (f <$> a)

-- 生成T1 a的随机值:接受a类型的生成器,返回T1 a的生成器
genT1 :: Gen a -> Gen (T1 a)
genT1 genA = T1 <$> fn @Int genA

代码解释:

  • TypeApplications扩展是为了显式指定fn的输入类型为Int(fn是多态函数,需要明确告诉它我们要生成Int -> a的函数)。
  • fn @Int genA会生成一个随机的Int -> a函数:它通过随机采样有限个Int输入和对应的a输出,模拟任意函数的行为,足够覆盖属性测试的需求。
  • 最后用T1 <$>把生成的函数包装成T1类型。

3. 在GHCi中运行属性测试

加载上述代码后,直接执行以下命令即可测试Functor实例是否符合函子律:

> lawsCheck (functorLaws genT1)

关于你提到的两个实例的说明:

其实你写的两个实例是完全等价的——因为对于函数类型(->) Int来说,它本身就是Functor,f <$> a的定义就是f . a。所以这两个实例都会通过函子律的测试,不存在错误版本。如果你想找一个违反函子律的实例,可以试试这个:

-- 错误实例,会违反函子律
instance Functor T1 where
  fmap f (T1 a) = T1 (\x -> f (a (x + 1)))

这个实例会在测试中失败,因为它不满足fmap id = id的核心律。

内容的提问来源于stack exchange,提问作者Jezen Thomas

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.29 16:22:39