我现在正在阅读“Haskell Book”的monoid部分,作者使用QuickCheck检查幺半群定律。在我自己写完一些东西之后,我有了这段代码:
module Main where
import Data.Monoid
import Test.QuickCheck
monoidAssoc :: (Eq m, Monoid m) => m -> m -> m -> Bool
monoidAssoc a b c = ((a <> b) <> c) == (a <> (b <> c))
monoidLeftId :: (Eq m, Monoid m) => m -> Bool
monoidLeftId a = (mempty <> a) == a
monoidRightId :: (Eq m, Monoid m) => m -> Bool
monoidRightId a = (a <> mempty) == a
type AssocCheck a = a -> a -> a -> Bool
type IdCheck a = a -> Bool
main :: IO ()
main = do
quickCheck (monoidAssoc :: AssocCheck (Maybe String))
quickCheck (monoidLeftId :: IdCheck (Maybe String))
quickCheck (monoidRightId :: IdCheck (Maybe String))
quickCheck (monoidAssoc :: AssocCheck [String])
quickCheck (monoidLeftId :: IdCheck [String])
quickCheck (monoidRightId :: IdCheck [String])
正如你所看到的,main
函数有两个几乎相同的块,我希望将它们简化为这样的:
checkMonoid :: (Eq m, Monoid m) => m -> IO ()
checkMonoid = do
quickCheck (monoidAssoc :: AssocCheck m)
quickCheck (monoidLeftId :: IdCheck m)
quickCheck (monoidRightId :: IdCheck m)
main :: IO ()
main = do
checkMonoid :: Maybe String -> IO ()
checkMonoid :: [String] -> IO ()
但这显然不起作用。我想要的是以某种方式将类型传递给checkMonoid
函数,因此quickCheck
函数将知道必须生成哪些arbitrary
数据。
答案 0 :(得分:3)
这正是TypeApplication
允许您执行的操作 - 您可以显式传递m
的类型。然后,您还需要ScopedTypeVariables
来确保m
内的所有checkMonoid
都相同,AllowAmbiguousTypes
让GHC知道您对此事实感到满意如果没有类型应用程序,checkMonoid
将不明确。
{-# LANGUAGE TypeApplications, AllowAmbiguousTypes, ScopedTypeVariables #-}
-- ...
checkMonoid :: forall m. (Eq m, Monoid m, Show m, Arbitrary m) => IO ()
checkMonoid = do
quickCheck (monoidAssoc :: AssocCheck m)
quickCheck (monoidLeftId :: IdCheck m)
quickCheck (monoidRightId :: IdCheck m)
main :: IO ()
main = do
checkMonoid @(Maybe String)
checkMonoid @[String]
TypeApplications
背后的原理是,Haskell正在将多态函数转换为也将类型作为参数(see more here)的函数 - 通常GHC负责确定要填充的类型参数使用TypeApplications
,@SomeType
说&#34;让前面函数调用的第一个类型参数为SomeType
&#34;。
通过forall m.
我确保GHC会做上述事情。然后,当我致电checkMonoid
时,我会明确传递m
的类型。