2011-03-16 14 views
5

Estoy pasando por Write Yourself a Scheme in Haskell. Es un gran tutorial, pero me he encontrado en una pared con una de las parsing exercises:Monadic tipo confusión

parseNumber :: Parser LispVal 
parseNumber = liftM (Number . read) $ many1 digit 

reescritura parseNumber usando:

  1. Do-notación
  2. secuenciación explícita con el operador = >>

no he tenido problemas con la notación do-:

parseNumber :: Parser LispVal 
parseNumber = do x <- many1 digit 
       let y = read x 
       return $ Number y 

Para # 2 Yo he probado un montón de variaciones, tales como:

parseNumber :: Parser LispVal 
parseNumber = (many1 digit) >>= (liftM (Number . read)) 

pero sigo corriendo a errores de tipo. Tengo dos preguntas.

  1. ¿Por qué me aparecen los errores de tipo? ¿Estoy entendiendo mal al operador de enlace monádico?
  2. ¿Por qué NO obtengo errores de tipo similar con mi solución de notación do?

Siento que me falta un concepto fundamental con respecto a los tipos?

Respuesta

10

Está intentando una transformación no trivial de la notación de do a la anotación de enlace, le recomiendo hacerlo de forma "trivial", y luego hacerlo sin puntos.

Recall:

 
x <- m === m >>= \x -> 
let x = e === let x = e in 

Entonces usted tiene:

 
parseNumber = many1 digit >>= \x -> 
       let y = read x in 
       return (Number y) 

(He quitado la $ para evitar problemas de precedencia.)

Entonces podemos convertir esto en:

 
parseNumber = many1 digit >>= \x -> return (Number (read x)) 
      = many1 digit >>= return . Number . read 

Ahora, si desea utilizar liftM, debe dejar de usar bind, ya que la función de elevación espera un valor monádico como argumento.

 
parseNumber = liftM (Number . read) (many1 digit) 
+0

maravillosa respuesta. Ahora tiene mucho más sentido. – dbyrne

2

En su caso, se unen tiene que escribir:

(>>=) :: Parser a -> (a -> Parser b) -> Parser b 

(ya que estás usando Parser como la Mónada)

Usted da unen dos argumentos: el primero, many1 digit, debe estar bien (con respecto al tipo); pero el tipo del segundo argumento es el tipo de resultado de liftM, es decir Parser a -> Parser b y esto hace no se ajusta al tipo esperado del segundo argumento (a -> Parser b)!

Sin haber probado que: en lugar de utilizar liftM (Number.read) como segundo argumento de bind, trate de usar return . Number . read - esto debe tener el tipo correcto y da probablemente lo que usted quiere ...