2012-07-29 11 views
8

私は99のhaskell問題をやり始めました。私はproblem 7にいて、私のunittestsは爆発していました。私にmonomorphism制限を教えてください?

どうやら、それはこのによるものです:http://www.haskell.org/haskellwiki/Monomorphism_restriction

私はちょうど私はちょっと混乱しているので、私はこれを正しく理解してくださいしたかったです。

状況1:func aは、型定義がないか厳密でない型定義で定義され、一度使用された場合、コンパイル時に型の推論に問題はありません。

状況2:同じ関数func aがプログラムで何度も使用されていますが、コンパイラは与えられた引数の関数を再計算しない限り、型が何であるか100%確信できません。

計算の損失を避けるために、ghcは正しく動作するためにはa に厳密な型定義が必要であるという文句をプログラマに伝えます。 test3が、私はそれがtestcase3の復帰のための2つの可能なタイプを持っていたことを言うように解釈することを定義したとき、私は私の状況で考える

assertEqualは(ショーを

assertEqual :: (Eq a, Show a) => String -> a -> a -> Assertion 

私はエラーを得たのタイプのDEFを持ちますそしてEq)、続ける方法を知らなかった。

これは正しいと思いますか、完全にオフですか?

problem7.hs:

-- # Problem 7 
-- Flatten a nested list structure. 

import Test.HUnit 

-- Solution 

data NestedList a = Elem a | List [NestedList a] 

flatten :: NestedList a -> [a] 
flatten (Elem x) = [x] 
flatten (List x) = concatMap flatten x 

-- Tests 

testcase1 = flatten (Elem 5) 
assertion1 = [5] 

testcase2 = flatten (List [Elem 1, List [Elem 2, List [Elem 3, Elem 4], Elem 5]]) 
assertion2 = [1,2,3,4,5] 

-- This explodes 
-- testcase3 = flatten (List []) 

-- so does this: 
-- testcase3' = flatten (List []) :: Eq a => [a] 

-- this does not 
testcase3'' = flatten (List []) :: Num a => [a] 

-- type def based off `:t assertEqual` 
assertEmptyList :: (Eq a, Show a) => String -> [a] -> Assertion 
assertEmptyList str xs = assertEqual str xs [] 

test1 = TestCase $ assertEqual "" testcase1 assertion1 
test2 = TestCase $ assertEqual "" testcase2 assertion2 
test3 = TestCase $ assertEmptyList "" testcase3'' 

tests = TestList [test1, test2, test3] 

-- Main 
main = runTestTT tests 

第一状況:testcase3 = flatten (List [])

GHCi, version 7.4.2: http://www.haskell.org/ghc/ :? for help 
Loading package ghc-prim ... linking ... done. 
Loading package integer-gmp ... linking ... done. 
Loading package base ... linking ... done. 
[1 of 1] Compiling Main    (problem7.hs, interpreted) 

problem7.hs:29:20: 
    Ambiguous type variable `a0' in the constraints: 
     (Eq a0) 
     arising from a use of `assertEmptyList' at problem7.hs:29:20-34 
     (Show a0) 
     arising from a use of `assertEmptyList' at problem7.hs:29:20-34 
    Probable fix: add a type signature that fixes these type variable(s) 
    In the second argument of `($)', namely 
     `assertEmptyList "" testcase3' 
    In the expression: TestCase $ assertEmptyList "" testcase3 
    In an equation for `test3': 
     test3 = TestCase $ assertEmptyList "" testcase3 
Failed, modules loaded: none. 
Prelude> 

第二の状況:testcase3 = flatten (List []) :: Eq a => [a]

GHCi, version 7.4.2: http://www.haskell.org/ghc/ :? for help 
Loading package ghc-prim ... linking ... done. 
Loading package integer-gmp ... linking ... done. 
Loading package base ... linking ... done. 
[1 of 1] Compiling Main    (problem7.hs, interpreted) 

problem7.hs:22:13: 
    Ambiguous type variable `a0' in the constraints: 
     (Eq a0) 
     arising from an expression type signature at problem7.hs:22:13-44 
     (Show a0) 
     arising from a use of `assertEmptyList' at problem7.hs:29:20-34 
    Possible cause: the monomorphism restriction applied to the following: 
     testcase3 :: [a0] (bound at problem7.hs:22:1) 
    Probable fix: give these definition(s) an explicit type signature 
        or use -XNoMonomorphismRestriction 
    In the expression: flatten (List []) :: Eq a => [a] 
    In an equation for `testcase3': 
     testcase3 = flatten (List []) :: Eq a => [a] 
Failed, modules loaded: none. 

答えて

4

それがあいまいなの解像度だ、そんなに単相性制限はありませんタイプ変数:defaulting hatによってコンパイルエラーが発生します。

-- This explodes 
-- testcase3 = flatten (List []) 

-- so does this: 
-- testcase3' = flatten (List []) :: Eq a => [a] 

-- this does not 
testcase3'' = flatten (List []) :: Num a => [a] 

flatten :: NestedList a -> [a] 
flatten (Elem x) = [x] 
flatten (List x) = concatMap flatten x 

flattena変数の型に制約を課していないので、などtestcase3の定義には問題はありません、それは多形だろう。

しかし、あなたはtest3でそれを使用し、

test3 = TestCase $ assertEmptyList "" testcase3 -- '' 

あなたは

assertEmptyList :: (Eq a, Show a) => String -> [a] -> Assertion 

の制約を継承今コンパイラがタイプtestcase3が使用されるべきで見つける必要があります。型を決定するのに十分なコンテキストがないため、コンパイラは、デフォルト変数によって型変数を解決しようとします。 defaulting rulesによると、コンテキスト(Eq a, Show a)は、少なくとも1つの数値クラスを含むコンテキストのみがデフォルトに適格であるため、デフォルトでは解決できません。したがって、あいまいな型変数のためにコンパイルが失敗します。

testcase3'およびは、左側に継承された定義の右側に制約を課す表現型シグネチャのために、単相性の制約に該当します。

testcase3'は、それがアサーションで使用されているかどうかにかかわらず、コンパイルに失敗します。

testcase3''は、式タイプシグネチャが数値制約を課すため、デフォルトで[Integer]になります。したがって、型がtestcase''のために単形化されている場合、制約型変数はデフォルトでIntegerになります。それで、それがtest3で使用されるタイプの問題はありません。

あなたは、両方の値が多型の値に自分でコンパイルしているだろうが、それでも唯一testcase3''test3で使用可能になり、バインディングに代わりの右側に

testcase3' :: Eq a => [a] 
testcase3' = flatten (List []) 

testcase3'' :: Num a => [a] 
testcase3'' = flatten (List []) 

を型シグネチャを与えていた場合これは、デフォルト設定を可能にするために必要な数値制約が導入されるためです。

関連する問題