2012-07-29 4 views
8

Ich begann 99 Haskell Probleme zu tun und ich war auf problem 7 und meine Unittests explodierten.Monomorphie-Beschränkung für mich bitte erklären?

Offenbar ist es aufgrund dieser: http://www.haskell.org/haskellwiki/Monomorphism_restriction

Ich wollte nur sicherstellen, dass ich das richtig verstanden, weil ich ein bisschen verwirrt bin.

situation 1: func a wird ohne typ def oder mit einem nicht strikten typ def definiert und dann einmal verwendet, der compiler hat keine probleme, die den typ zur kompilierungszeit ableiten.

situation 2: das gleiche func a wird oft im Programm verwendet, der Compiler kann nicht zu 100% sicher sein, was der Typ ist, wenn er die Funktion für die gegebenen Argumente nicht neu berechnet.

Um den Berechnungsverlust zu vermeiden, beschwert sich ghc dem Programmierer, dass es einen strikten Typ def auf a benötigt, um richtig zu arbeiten.

ich in meiner Situation denken, assertEqual hat den Typ def von

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

Ich erhalte eine Fehlermeldung, wenn test3 definiert wurde, dass ich mit den Worten interpretiert, dass es für die Rückkehr von testcase3 2 möglichen Typen hatte (Zeige und Eq) und wusste nicht weiter.

Klingt das richtig oder bin ich komplett ausgeschaltet?

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 

1. Situation: 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> 

2. Situation: 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. 

Antwort

4

Es ist nicht so sehr die Monomorphie Einschränkung, es ist die Auflösung von mehrdeutigen Typ Variablen von defaulting t Hat verursacht den Kompilierungsfehler.

-- 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 

flatten keine Einschränkungen für die Art erlegt Variable a, so gibt es kein Problem mit der Definition von testcase3 als solche wäre es polymorph sein.

Aber wenn Sie es in test3,

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

erben Sie die Einschränkungen von

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

Jetzt ist der Compiler aus, bei welcher Typ finden hat testcase3 sollte verwendet werden. Es gibt nicht genügend Kontext, um den Typ zu bestimmen, daher versucht der Compiler standardmäßig, die Typvariable aufzulösen. Gemäß der defaulting rules kann ein Kontext (Eq a, Show a) nicht standardmäßig aufgelöst werden, da nur Kontexte, die mindestens eine numerische Klasse enthalten, für die Standardisierung in Frage kommen. Daher schlägt die Kompilierung aufgrund einer mehrdeutigen Typvariablen fehl.

testcase3' und testcase3'' fallen jedoch unter die Monomorphie-Einschränkung aufgrund der Ausdruckssignatur, die Einschränkungen auf der rechten Seite der Definition auferlegt, die von der linken geerbt werden.

testcase3' kann daher nicht kompiliert werden, unabhängig davon, ob es in einer Zusicherung verwendet wird.

testcase3'' wird auf [Integer] voreingestellt, da die Ausdruckssignatur eine numerische Einschränkung auferlegt. Wenn also der Typ für testcase'' monomorphisiert wird, wird die Variable des eingeschränkten Typs standardmäßig auf Integer gesetzt. Dann ist die Art, in der es in test3 verwendet wird, keine Frage.

Wenn Sie Typ Signaturen zu den Bindungen statt auf der rechten Seite,

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

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

beide Werte gegeben hatte, sich auf ihre eigenen polymorphen Werte zusammengestellt haben würde, aber nach wie vor nur testcase3'' würde in test3 verwendbar sein, da nur dies die erforderliche numerische Einschränkung einführt, um den Standard zuzulassen.

Verwandte Themen