2016-10-14 142 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的類型。