2016-10-14 101 views
1

我正在读“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

回答

3

这正是TypeApplication可以让你做的事情 - 你可以明确地传递类型为m。然后您还需要ScopedTypeVariables以确保您checkMonoid内部的所有m都是相同的,并且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说“让第一个类型参数为前面的函数调用为SomeType”。

通过使forall m.我确保GHC将执行上述操作。然后,当我拨打checkMonoid时,我明确地通过了m的类型。