2012-07-29 12 views
8

Empecé a hacer 99 problemas de haskell y estaba en problem 7 y mis unittest estaban explotando.Explícame la restricción de monomorfismo por favor?

Al parecer, es debido a esto: http://www.haskell.org/haskellwiki/Monomorphism_restriction

sólo quería asegurarse de que entiende esto correctamente, porque estoy un poco confundido.

situación 1: func a se define sin tipo def o con un tipo def no estricto y luego se utiliza una vez, el compilador no tiene problemas para deducir el tipo en tiempo de compilación.

situación 2: el mismo func a se usa muchas veces en el programa, el compilador no puede estar 100% seguro de lo que es el tipo a menos que vuelva a calcular la función para los argumentos dados.

Para evitar la pérdida de cómputo, ghc se queja con el programador de que necesita un tipo def estricto en a para funcionar correctamente.

Creo que en mi situación, assertEqual tiene la def tipo de

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

que estaba recibiendo un error cuando test3 se definió que interpreté como diciendo que tenía 2 tipos posibles para el retorno de testcase3 (Mostrar y Eq) y no sabía cómo continuar.

¿Eso suena correcto o estoy completamente apagado?

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 

primera situación: 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> 

segunda situación: 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. 

Respuesta

4

Está no es tanto la restricción monomorphism, que es la resolución de ambigüedad escriba variables por defaulting t hat causa la falla de compilación.

-- 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 no impone restricciones sobre el tipo de variable a, así que no hay problema con la definición de testcase3 como tal, sería polimórfica.

Sin embargo, cuando se utiliza en test3,

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

que heredar las restricciones de

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

Ahora el compilador tiene que averiguar a qué tipo testcase3 ser utilizado allí. No hay suficiente contexto para determinar el tipo, por lo que el compilador intenta resolver la variable de tipo de forma predeterminada. De acuerdo con defaulting rules, un contexto (Eq a, Show a) no se puede resolver de forma predeterminada, ya que solo los contextos que involucran al menos una clase numérica son elegibles para el incumplimiento. Entonces la compilación falla debido a una variable de tipo ambigua.

testcase3' y testcase3'' caen bajo la restricción de monomorfismo debido a la firma del tipo de expresión que impone restricciones en el lado derecho de la definición que son heredadas por la izquierda.

testcase3' no se puede compilar debido a eso, independientemente de si se utiliza en una aserción.

testcase3'' se establece de forma predeterminada en [Integer] ya que la firma del tipo de expresión impone una restricción numérica. Por lo tanto, cuando el tipo se monomorfiza para testcase'', la variable de tipo restringida se predetermina a Integer. Entonces no hay duda del tipo en que se usa en test3.

Si se hubiera dado las firmas de tipos a los enlaces en lugar de a la derecha,

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

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

ambos valores habría compilado por su cuenta a los valores polimórficas, pero sólo testcase3'' sería utilizable en test3, ya que solo eso introduce la restricción numérica requerida para permitir el incumplimiento.

Cuestiones relacionadas