当您没有输入参数来告诉您类型时,基于类型创建事物数组
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
您甚至不需要特别指定 floatTests
和 doubleTests
;你可以这样写
allTests = numberTests @Float ++ numberTests @Double
如果你愿意的话。
有时我想运行 对一组不同类型进行一组测试。这是我通常如何做的一个简单示例:
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
您甚至不需要特别指定 floatTests
和 doubleTests
;你可以这样写
allTests = numberTests @Float ++ numberTests @Double
如果你愿意的话。