несколько функций с одинаковым интерфейсом и переиспользовать интерфейс?
И еще считается ли нормальной практикой использование подобных конструкций?
newtype Parser a = Parser
{ runParser :: Input -> Either ParserError (Input, a)
}
Приведите пример функций с одинаковым интерфейсом?
Ну типа parseString :: String -> Maybe (String, JValue) parseNumber :: String -> Maybe (String, JValue)
А что не устраивает в parseString/parseNumber?
Это делается не для создания "одинакового интерфейса" (тут бы и обычный type подошёл), а ещё для разграничения сути задачи от общей комбинаторной обвязки. Например есть примитивные парсеры которые имеют тип Parser a, а есть производные, с типами вида Param -> Pam -> Pam -> Parser a. Без такого разграничения было бы Param -> Pam -> Pam -> Input -> Either Err (Input, a). Получилась функция из параметров и инпута в Either. Хотя на самом деле имеется в виду функция из параметров в парсер. Да, каррирование, но с явным разграничением это более ясно видно. И в теле функции становится проще понимать где что. Например, задача "нужен парсер для огурцов. С обёрткой прям сходу можно начать писать: atomicCucumbers :: Parse [Cucumber] atomicCucumbers = Parser \input -> error "to-do" И ещё сразу видно, что единица композиции тут не Either Err (Input, a), как иногда некоторые думают, когда надо писать инстансы, а именно что функция из инпута. И если в парсер ещё нет кастомных ошибок или инпута, то без newtype обёртки - привет орфаны: instance Monoid ([Char] -> ([Char], a)) where ... В общем это тот случай когда нормально писать проще, чем плохо.
Обсуждают сегодня