Pergunta

Eu comecei a fazer 99 haskell problemas e eu estava no problema 7 e o meu unittests foram explodir.

Ao que parece, é devido a este: http://www.haskell.org/haskellwiki/Monomorphism_restriction

Eu só queria ter certeza de que eu entendi isso corretamente, porque eu estou meio confuso.

situação 1:func a é definido com nenhum tipo de def ou com um não-estrita de tipo de def e, em seguida, usado uma vez, o compilador não tem problemas, inferir o tipo em tempo de compilação.

situação 2:a mesma func a é utilizado muitas vezes no programa, o compilador não pode ser 100% de certeza de que o tipo é menos que recalcula a função para os argumentos fornecidos.

Para evitar o cálculo de perda, ghc reclama para o programador que ele precisa de um tipo estrito de def no a para funcionar corretamente.

Eu acho que na minha situação, assertEqual tem o tipo de def

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

Eu estava ficando um erro quando test3 ficou definido que eu interpretada como dizendo que ele tinha 2 tipos possíveis para o retorno de testcase3 (Mostrar) e Eq (equalizador) e não sabia como continuar.

Será que o som correto ou estou completamente fora?

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ª situação: 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ª situação: 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.
Foi útil?

Solução

Não é tanto o monomorphism restrição, é a resolução do ambíguo tipo de variáveis incumprimento que faz com que a compilação falha.

-- 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 impõe nenhuma restrição sobre o tipo de variável a, então não há nenhum problema com a definição de testcase3 como tal, seria polimórficos.

Mas quando você usá-lo em test3,

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

você herdar restrições do

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

Agora, o compilador tem de descobrir em que tipo de testcase3 deve ser usada.Não é suficiente o contexto para determinar o tipo, para que o compilador tenta resolver o tipo de variável em situação de incumprimento.De acordo com o incumprimento de regras de, contexto (Eq a, Show a) não podem ser resolvidos pelo inadimplente, uma vez que apenas os contextos que envolvam pelo menos um numéricos classe são elegíveis para o inadimplente.Assim, a compilação falha devido a um modelo de tipo de variável.

testcase3' e testcase3'' no entanto cair sob o monomorphism restrição devido à expressão do tipo de assinatura que impõe restrições no lado direito da definição do que são herdadas pela esquerda.

testcase3' falha ao compilar devido a que, independentemente de se ele é usado em uma declaração.

testcase3'' fica padronizada para [Integer] uma vez que a expressão do tipo de assinatura impõe uma numérico restrição.Assim, quando o tipo é monomorphised para testcase'', restrita tipo de variável é padrão para Integer.Em seguida, não existe nenhuma pergunta do tipo que é usado em test3.

Se você tivesse dado tipo de assinaturas para as ligações em vez de para o lado direito,

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

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

ambos os valores teria compilado por conta própria para polimórficos valores, mas ainda assim, apenas testcase3'' seria utilizável no test3, pois só a que apresenta o necessário numérico restrição para permitir situação de incumprimento.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top