ок, может это поможет. Давайте начнем с того, что вернем некоторые точки обратно в dePsr
.
dePsr :: Parser a -> String -> Maybe (String, a)
dePsr (PsrOf p) rest = p rest
И давайте также выпишем возвращение: (NB я добавляю все пункты для ясности)
return :: a -> Parser a
return a = PsrOf (\rest -> Just (rest, a))
А теперь из Just
ветви (>>=)
определения
Just (rest, a) -> dePsr (k a) rest
Давайте удостоверимся, что мы согласны с тем, что каждая вещь:
rest
строка, оставшаяся непарсированной после применения p1
a
Результат применения p1
k :: a -> Parser b
берет результат предыдущего парсера и создает новый парсер
dePsr
разворачивает Parser a
обратно в функцию `String -> Maybe (String, a)
Помните, что мы обернем это обратно в синтаксический анализатор в верхней части функции: PsrOf q
Таким образом, в английском языке bind (>>=)
принимает синтаксический анализатор в a
и функцию из a
в синтаксический анализатор в b
и возвращает синтаксический анализатор в b
. Полученный синтаксический анализатор создается путем переноса q :: String -> Maybe (String, b)
в конструктор Parser PsrOf
. Затем q
, объединенный синтаксический анализатор, принимает String
, называемый inp
, и применяет функцию p1 :: String -> Maybe (String,a)
, которую мы получили из сопоставления с образцом, к первому анализатору, и сопоставление с образцом в результате. За ошибку выкладываем Nothing
(легко). Если у первого парсера был результат, у нас есть буксируемые фрагменты информации, еще непарсированная строка с именем rest
и результат a
. Мы даем a
на k
, второй комбинатор синтаксического анализатора, и получаем Parser b
, который нам нужно развернуть с помощью dePsr
, чтобы получить функцию (String -> Maybe (String,b)
назад. Эту функцию можно применить к rest
для окончательный результат комбинированных парсеров.
Я думаю, что самое сложное в чтении это то, что иногда мы карри выполняем функцию синтаксического анализа, которая скрывает то, что на самом деле происходит.
Хорошо для satisfy
примера
satisfy pred
= anyChar >>= (c -> if pred c then return c else empty)
empty
происходит из альтернативного экземпляра и является PsrOf (const Nothing)
, поэтому анализатор всегда завершается ошибкой.
Давайте посмотрим только на успешные ветки. Подменой только удачной части:
PsrOf (\(c:cs) ->Just (cs, c)) >>= (\c -> PsrOf (\rest -> Just (rest, c)))
Так что в связке (>>=)
определение
p1 = \(c:cs -> Just (cs, c))
k = (\c -> PsrOf (\rest -> Just (rest, c)))
q inp = let Just (rest,a) = p1 inp in dePsr (k a) rest
снова только удачная ветка
Тогда q
становится
q inp =
let Just (rest, a) = (\(c:cs) -> Just (cs, c)) inp
in dePsr (\c -> PsrOf (\rest -> Just (rest, c))) a rest
Делаем небольшое β-сокращение
q inp =
let (c:cs) = inp
rest = cs
a = c
in dePsr (PsdOf (\rest -> Just (rest, a))) rest -- dePsr . PsrOf = id
Наконец-то убираю еще
q (c:cs) = Just (cs, c)
Таким образом, если pred
успешен, мы уменьшаем satisfy
до точно anyChar
, что является именно тем, что мы ожидаем, и именно то, что мы находим в первом примере вопроса. Я оставлю это как и укажу читателю (читай: я ленивый), чтобы доказать, что если inp = ""
или pred c = False
, то результат будет Nothing
, как в первом satisfy
примере.
ПРИМЕЧАНИЕ. Если вы делаете что-то кроме назначения класса, вы сэкономите часы боли и разочарования, начав с обработки ошибок с самого начала, чтобы ваш синтаксический анализатор String -> Either String (String,a)
легко сделал тип ошибки более общим позже , но PITA, чтобы изменить все с Maybe
на Either
.
Вопрос:"[C] не могли бы вы объяснить, как вы пришли к return a = PsrOf (\rest -> Just (rest, a))
из return = pure
после того, как вы вернули« очки »обратно?
Ответ: Прежде всего, довольно неудачно дать определение экземпляра Monad без определений Functor и Applicative. Функции pure
и return
должны быть идентичны (это является частью законов Монады), и их можно было бы назвать одним и тем же, за исключением Monad
, значительно предшествовавшего Applicative в истории Haskell. На самом деле, я не «знаю», как выглядит чистый, но я знаю, что это должно быть, потому что это единственно возможное определение. (Если вы хотите понять доказательство этого утверждения, я прочитал документы и знаю результаты, но мне не достаточно набранных лямбда-исчислений, чтобы быть уверенными в воспроизведении результатов.)
return
должен переносить значение в контексте без изменения контекста.
return :: Monad m => a -> m a
return :: a -> Parser a -- for our Monad
return :: a -> PsrOf(\str -> Maybe (rest, value)) -- substituting the constructor (PSUDO CODE)
A Parser
- это функция, которая принимает строку для анализа и возвращает Just
значение вместе с любой непарсированной частью исходной строки или Nothing on failure, all wrapped in the constructor
PsrOf . The context is the string to be parsed, so we cannot change that. The value is of course what was passed to
return`.Синтаксический анализатор всегда завершается успешно, поэтому мы должны вернуть только значение.
return a = PsrOf (\rest -> Just (rest, a))
rest
- это контекст, и он передается без изменений.a
- это значение, которое мы помещаем в контекст Monad.
Для полноты здесь также есть единственное разумное определение fmap
из Functor.
fmap :: Functor f => (a->b) -> f a -> f b
fmap :: (a -> b) -> Parser a -> Parser b -- for Parser Monad
fmap f (PsrOf p) = PsrOf q
where q inp = case p inp of
Nothing -> Nothing
Just (rest, a) -> Just (rest, f a)
-- better but less instructive definition of q
-- q = fmap (\(rest,a) -> (rest, f a)) . p