Путаница монадического типа
Я собираюсь через написать себе схему в Haskell. Это отличный учебник, но я наткнулся на стену с одним из упражнений разбора:
parseNumber :: Parser LispVal
parseNumber = liftM (Number . read) $ many1 digit
Перепишите parseNumber, используя:
- Do-нотация
- явное секвенирование с помощью оператора > > =
У меня не было проблем с до-нотацией:
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))
Но я постоянно сталкиваюсь с ошибками типа. У меня их два вопросы.
- Почему я получаю ошибки типа? Я неправильно понимаю оператор монадической привязки?
- Почему я не получаю подобных ошибок типа с моим решением do-notation?
Мне кажется, что я упускаю фундаментальное понятие о типах?
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
- это должно иметь правильный тип и дает вероятно, что вы хотите...