当您没有输入参数来告诉您类型时,基于类型创建事物数组

Creating an array of things based on a type when you don't have an input parameter to tell you the type

有时我想运行 对一组不同类型进行一组测试。这是我通常如何做的一个简单示例:

import           Test.Framework                                (Test)
import           Test.Framework.Providers.QuickCheck2          (testProperty)
import           Test.QuickCheck

additionCommutes :: (Eq a, Num a) => a -> a -> Bool
additionCommutes x y = x + y == y + x

divisionByItselfIsOne :: (Eq a, Ord a, Fractional a) => a -> Property
divisionByItselfIsOne x = x > 0 ==> x / x == 1
  -- pretend rounding errors won't occur, this is just an example


floatTests :: [Test]
floatTests = [
               testProperty "float addition commutes"
                 (additionCommutes :: Float -> Float -> Bool),
               testProperty "float divided by itself is 1"
                 (divisionByItselfIsOne :: Float -> Property)
             ]

doubleTests :: [Test]
doubleTests = [
               testProperty "double addition commutes"
                 (additionCommutes :: Double -> Double -> Bool),
               testProperty "double divided by itself is 1"
                 (divisionByItselfIsOne :: Double -> Property)
             ]

但我宁愿避免重复列出每种类型的测试。 (也许涉及很多测试。)我想一次定义测试列表,然后为每种类型实例化它。像这样...

numberTests :: (Eq a, Ord a, Num a, Fractional a) => [Test] for the type "a"
numberTests = [
                testProperty "double addition commutes"
                  (additionCommutes :: a -> a -> Bool),
                testProperty "double divided by itself is 1"
                  (divisionByItselfIsOne :: a -> Property)
               ]


floatTests = numberTests :: [Test] for the type "float"

doubleTests = numberTests :: [Test] for the type "double"

当然,那是无效的 Haskell。 我觉得可能有一些神奇的类型级编程技术可以用来完成这个。我浏览了 Maguire 的 Thinking With Types 但我仍然看不出如何解决这个问题。有什么建议吗?

如果有一种技术在原则上可行,但不能很好地与 QuickCheck 的反射功能配合使用,那很好——与解决这个特定问题相比,我更感兴趣的是培养我的类型级编程技能。

你很接近:

{-# Language AllowAmbiguousTypes #-}
{-# Language ScopedTypeVariables #-}
{-# Language TypeApplications #-}

--             vvvvvvvvv
numberTests :: forall a. (Eq a, Ord a, Num a, Fractional a) => [Test]
numberTests = -- just like in the question

--                        vvvvvvv
floatTests  = numberTests @Float
doubleTests = numberTests @Double

您甚至不需要特别指定 floatTestsdoubleTests;你可以这样写

allTests = numberTests @Float ++ numberTests @Double

如果你愿意的话。