使用QuickCheck,可以编写参数多态属性,如下所示:
associativityLaw :: (Eq a, Show a, Semigroup a) => a -> a -> a -> Property
associativityLaw x y z = (x <> y) <> z === x <> (y <> z)这只是一个例子,因为我的实际属性更复杂,但它充分说明了这个问题。此属性验证对于类型a,<>运算符是否是关联的。
想象一下,我想要使用这个属性超过一种类型。我可以这样定义我的测试列表:
tests =
[
testGroup "Monoid laws" [
testProperty "Associativity law, [Int]" (associativityLaw :: [Int] -> [Int] -> [Int] -> Property),
testProperty "Associativity law, Sum Int" (associativityLaw :: Sum Int -> Sum Int -> Sum Int -> Property)
]
]这是可行的,但觉得不必要的冗长。我希望能够简单地声明,对于给定的属性,a应该是[Int],或者a应该是Sum Int。
类似于假设的语法:
testProperty "Associativity law, [Int]" (associativityLaw :: a = [Int]),
testProperty "Associativity law, Sum Int" (associativityLaw :: a = Sum Int)有没有办法做到这一点,也许通过一个GHC语言扩展?
我的实际问题涉及类型较高的类型,我希望能够说明,例如,f a是[Int],f a是Maybe String。
我知道this answer,但这两种选项(Proxy和Tagged)看起来,至少在这里描述过,太尴尬了,以至于无法真正解决这个问题。
发布于 2018-10-03 13:11:51
您可以使用TypeApplications绑定类型变量,如下所示:
{-# LANGUAGE TypeApplications #-}
associativityLaw @[Int]在您提到的具有较高类型并希望将f a绑定到[Int]的情况下,必须分别绑定类型变量f和a:
fmap @[] @Int对于具有多个类型变量的函数,可以按顺序应用args:
f :: a -> b -> Int
-- bind both type vars
f @Int @String
-- bind just the first type var, and let GHC infer the second one
f @Int
-- bind just the second type var, and let GHC infer the first one
f @_ @String有时,类型变量的“顺序”可能并不明显,但您可以使用:type +v并向GHCi查询更多信息:
λ> :t +v traverse
traverse
:: Traversable t =>
forall (f :: * -> *) a b.
Applicative f =>
(a -> f b) -> t a -> f (t b)在标准haskell中,类型变量的“顺序”并不重要,所以GHC只是为您提供一个。但在TypeApplications在场的情况下,这个命令确实很重要:
map :: forall b a. (a -> b) -> ([a] -> [b])
-- is not the same as
map :: forall a b. (a -> b) -> ([a] -> [b])因此,当您使用高度参数化的代码时,或者您希望您的用户希望在您的函数上使用TypeApplications时,您可能希望显式地设置您的类型vars的顺序,而不是让GHC使用ExplicitForAll为您定义一个订单。
{-# LANGUAGE ExplicitForAll #-}
map :: forall a b. (a -> b) -> ([a] -> [b])这感觉很像java或c#中的c#。
https://stackoverflow.com/questions/52627561
复制相似问题