Путаница монадического типа


Я собираюсь через написать себе схему в Haskell. Это отличный учебник, но я наткнулся на стену с одним из упражнений разбора:

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 6

2 ответа:

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

Напомним:

 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)

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

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

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

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

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