2012-07-29 21 views
8

Ho iniziato a fare 99 problemi con haskell ed ero su problem 7 e le mie attivazioni stavano esplodendo.Spiegare la limitazione del monomorfismo per me per favore?

quanto pare, è a causa di questo: http://www.haskell.org/haskellwiki/Monomorphism_restriction

Volevo solo per assicurarsi che ho capito correttamente questo perché io sono un po 'confuso.

situazione 1: func a è definito senza def di tipo o con un tipo non severo def e quindi utilizzato una sola volta, il compilatore non ha problemi a dedurre il tipo in fase di compilazione.

situazione 2: la stessa funzione a viene utilizzata molte volte nel programma, il compilatore non può essere sicuro al 100% di quale sia il tipo a meno che non ricalcoli la funzione per gli argomenti specificati.

Per evitare la perdita del calcolo, ghc lamenta al programmatore che è necessario un tipo rigoroso def su a per funzionare correttamente.

Credo che nella mia situazione, assertEqual ha il tipo DEF di

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

mi è stato sempre un errore quando test3 è stato definito che ho interpretato come dicendo che aveva 2 tipi possibili per il ritorno di testcase3 (Visualizza e Eq) e non sapevo come continuare.

Suona corretto o sono completamente spento?

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 

prima situazione: 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> 

seconda situazione: 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. 

risposta

4

E 'non tanto la restrizione monomorfismo, è la risoluzione delle ambiguità digitare variabili per defaulting t cappello causa il fallimento della compilazione.

-- 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 impone vincoli sul tipo variabile a, quindi non c'è alcun problema con la definizione di testcase3 quanto tale, sarebbe polimorfico.

Ma quando lo si utilizza in test3,

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

si ereditano i vincoli di

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

Ora il compilatore deve scoprire a quale tipo testcase3 dovrebbe essere usato lì. Non c'è abbastanza contesto per determinare il tipo, quindi il compilatore tenta di risolvere la variabile di tipo per impostazione predefinita. Secondo lo defaulting rules, un contesto (Eq a, Show a) non può essere risolto per impostazione predefinita, poiché solo i contesti che coinvolgono almeno una classe numerica sono idonei per l'impostazione predefinita. Quindi la compilazione fallisce a causa di una variabile di tipo ambigua.

testcase3' e testcase3'' tuttavia rientrano nella limitazione del monomorfismo dovuto alla firma del tipo di espressione che impone vincoli sul lato destro della definizione che sono ereditati dalla sinistra.

testcase3' non riesce a compilare a causa di ciò, indipendentemente dal fatto che sia utilizzato in un'asserzione.

testcase3'' diventa predefinito su [Integer] poiché la firma del tipo di espressione impone un vincolo numerico. Pertanto, quando il tipo viene monomorfizzato per testcase'', la variabile di tipo vincolata viene impostata su Integer. Quindi non si tratta del tipo in cui viene utilizzato in test3.

Se tu avessi dato tipo di firme per le associazioni invece che a destra,

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

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

entrambi i valori avrebbero compilato in proprio per i valori polimorfici, ma ancora solo testcase3'' sarebbe utilizzabile in test3, poiché solo questo introduce il vincolo numerico richiesto per consentire l'impostazione predefinita.