2011-12-17 7 views
20

Estoy tratando de pasar por el ejemplo YesNo del libro Learn You a Haskell for Great Good!.Variable de tipo ambiguo `a0 'en las restricciones

Aquí está mi código fuente:

module Main where 

main :: IO() 

main = putStrLn (show (yesno 12)) 

class YesNo a where 
    yesno :: a -> Bool 


instance YesNo Bool where 
    yesno b = b 

instance YesNo [a] where 
    yesno [] = False 
    yesno _ = True 


instance YesNo Int where 
    yesno 0 = False 
    yesno _ = True 

Cuando ejecuto el código siguiente excepción se produce:

Ambiguous type variable `a0' in the constraints: 
    (YesNo a0) arising from a use of `yesno' 
      at /Users/mkhadikov/Projects/personal/haskell/hello-world/yesno.hs:5:25-29 
    (Num a0) arising from the literal `12' 
      at /Users/mkhadikov/Projects/personal/haskell/hello-world/yesno.hs:5:31-32 
Probable fix: add a type signature that fixes these type variable(s) 
In the first argument of `show', namely `(yesno 12)' 
In the first argument of `putStrLn', namely `(show (yesno 12))' 
In the expression: putStrLn (show (yesno 12)) 

¿Puede usted explicar lo que está mal con este código?

Respuesta

31

¡El problema es que no sabe qué tipo es 12! Puede ser cualquier tipo con un ejemplo numérico:

Es necesario especificar el tipo que desea directamente: tratar putStrLn (show (yesno (12 :: Int))).

¿Por qué GHC no puede elegir Int, ya que ninguna otra opción funcionaría, usted pregunta? Buena pregunta. La respuesta es que con el sistema de clases de tipos de Haskell, agregar una instancia nunca puede invalidar los programas correctos existentes o cambiar su comportamiento. (Esto se conoce como supuestos del mundo abierto). Si seleccionó Int, ¿qué pasaría si agregara instance YesNo Integer? ¡La elección se volvería ambigua y tu programa se rompería!

Por lo tanto, si desea utilizar una clase de tipos como esta con un valor polimórfico, debe especificar qué tipo es más preciso. Esto no debería surgir mucho en la práctica, ya que generalmente habrá algún contexto circundante para obligar al tipo a ser lo que quieres; son principalmente literales numéricos los que se ven afectados por esto.

5

El problema es que 12 en realidad tiene el tipo Num a => a y no Int como era de esperar. Si agrega una anotación de tipo explícito como 12 :: Int, debería compilarse.

+0

¿Es posible crear su propio tipo de constructores de datos que funcionan como '12' en Haskell? Parece que cada vez que creas un constructor de datos, construye un valor de un solo tipo. Pero cuando escribe '12', como puede ver, no construye un valor de un solo tipo, sino un valor de cualquier tipo donde el tipo está restringido por Num. Por lo tanto 'Num a => a'. ¿Estoy interpretando esto correctamente? ¿Esto es como construir un valor que tiene una unión existencial? – CMCDragonkai

+0

@CMCDragonkai Estoy un poco confuso con los detalles, pero básicamente la clase de tipos 'Num' contiene una función' fromInteger :: Num a => Integer -> a'. Un literal numérico funciona como si se llamara 'fromInteger'. – fuz

+0

Ah, entonces el 'a' se convierte en una especie de unión/existencial. Muy interesante. – CMCDragonkai

1

Tuve el mismo problema.

Esta es una solución, tal vez no el mejor pero funciona:

class YesNo a where 
    yesno :: a -> Bool 


instance YesNo Int where 
    yesno 0 = False 
    yesno _ = True 


instance YesNo Integer where 
    yesno 0 = False 
    yesno _ = True 
Cuestiones relacionadas