Путаница монадического типа - PullRequest
6 голосов
/ 16 марта 2011

Я прохожу через Напишите себе схему на Хаскеле .Это отличный учебник, но я наткнулся на стену с одним из упражнений по синтаксическому анализу :

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

Перепишите parseNumber, используя:

  1. Do-нотация
  2. явная последовательность с оператором >> =

У меня не было проблем с нотацией:

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

Для # 2 я пробовалкуча вариантов, таких как:

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

, но я продолжаю сталкиваться с ошибками типов.У меня есть два вопроса.

  1. Почему я получаю ошибки типа?Я неправильно понимаю оператор монадического связывания?
  2. Почему я не получаю схожих ошибок типов с моим решением do-notation?

Мне кажется, что мне не хватает фундаментальной концепции относительно типов

Ответы [ 2 ]

11 голосов
/ 16 марта 2011

Вы пытаетесь нетривиальным преобразованием из нотации do в нотацию связывания, я рекомендую сделать это "тривиальным" способом, а затем сделать его бессмысленным.

Напомним:

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

Тогда у вас есть:

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

(я удалил $, чтобы избежать проблем с приоритетами.)

Затем мы можем преобразовать это в:

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

Теперь, если вы хотите использовать liftM, вам нужно прекратить использовать bind, поскольку поднятая функция ожидает монадическое значение в качестве аргумента.

 parseNumber = liftM (Number . read) (many1 digit)
2 голосов
/ 16 марта 2011

В вашем случае bind имеет тип:

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

(так как вы используете Parser в качестве монады)

Вы даете bind два аргумента: первый, many1 digit, должно быть в порядке (относительно типа);но тип второго аргумента является типом результата liftM, а именно Parser a -> Parser b, и это не соответствует ожидаемому типу второго аргумента (a -> Parser b)!

Не проверив его: вместо использования liftM (Number.read) в качестве второго аргумента связывания, попробуйте использовать return . Number . read - это должно иметь правильный тип и, вероятно, дает то, что вы хотите ...

...