Почему синтаксис newtype создает функцию

Я смотрю на это объявление:

newtype Parser a = Parser { parse :: String -> Maybe (a,String) }

Вот что я понимаю:

1) Парсер объявлен как тип с параметром типа a

2) Вы можете создать экземпляр Parser, предоставив функцию парсера, например p = Parser (\s -> Nothing)

Что я заметил, так это то, что внезапно у меня есть имя функции parse, и она способна запускать синтаксические анализаторы.

Например, я могу запустить:

parse (Parser  (\s -> Nothing)) "my input" 

и получите Nothing в качестве вывода.

Как эта функция синтаксического анализа была определена с этой конкретной сигнатурой? Как эта функция «знает», что нужно выполнить переданный ей синтаксический анализатор? Надеюсь, что кто-то может развеять мое замешательство.

Спасибо!


person Tomer    schedule 19.02.2020    source источник
comment
это синтаксис record, который автоматически создает функцию accessor.   -  person Will Ness    schedule 22.02.2020


Ответы (2)


Когда вы пишете newtype Parser a = Parser { parse :: String -> Maybe (a,String) }, вы вводите три вещи:

  1. Тип с именем Parser.

  2. Конструктор уровня терминов из Parser с именем Parser. Тип этой функции

Parser :: (String -> Maybe (a, String)) -> Parser a

Вы даете ему функцию, и он оборачивает ее в Parser

  1. Функция с именем parse для удаления оболочки Parser и возврата вашей функции. Тип этой функции:
parse :: Parser a -> String -> Maybe (a, String)

Проверьте себя в ghci:

Prelude> newtype Parser a = Parser { parse :: String -> Maybe (a,String) }
Prelude> :t Parser
Parser :: (String -> Maybe (a, String)) -> Parser a
Prelude> :t parse
parse :: Parser a -> String -> Maybe (a, String)
Prelude>

Ничего не стоит то, что конструктор уровня термина (Parser) и функция удаления оболочки (parse) являются произвольными именами и не должны совпадать с именем типа. Например, обычно пишут:

newtype Parser a = Parser { unParser :: String -> Maybe (a,String) }

это дает понять, что unParse удаляет оболочку вокруг функции синтаксического анализа. Однако я рекомендую, чтобы ваш тип и конструктор имели одно и то же имя при использовании newtypes.

Как эта функция «знает», что нужно выполнить переданный ей парсер?

Вы разворачиваете функцию с помощью parse, а затем вызываете развернутую функцию с помощью "myInput".

person Juan Pablo Santos    schedule 19.02.2020

Во-первых, давайте посмотрим на новый тип парсера без синтаксиса записи:

newtype Parser' a = Parser' (String -> Maybe (a,String))

Должно быть очевидно, что делает этот тип: он хранит функцию String -> Maybe (a,String). Чтобы запустить этот парсер, нам нужно будет создать новую функцию:

runParser' :: Parser' -> String -> Maybe (a,String)
runParser' (Parser' p) i = p i

И теперь мы можем запускать парсеры вроде runParser' (Parser' $ \s -> Nothing) "my input".

Но теперь обратите внимание, что, поскольку функции Haskell каррированы, мы можем просто удалить ссылку на ввод i, чтобы получить:

runParser'' :: Parser' -> (String -> Maybe (a,String))
runParser'' (Parser' p) = p

Эта функция в точности эквивалентна runParser', но вы можете подумать об этом по-другому: вместо того, чтобы явно применять функцию анализатора к значению, она просто берет анализатор и извлекает из него функцию анализатора; однако благодаря каррированию runParser'' по-прежнему можно использовать с двумя аргументами.

Теперь вернемся к исходному типу:

newtype Parser a = Parser { parse :: String -> Maybe (a,String) }

Единственная разница между вашим типом и моим заключается в том, что ваш тип использует синтаксис записи, хотя это может быть немного сложно распознать, поскольку newtype может иметь только одно поле; этот синтаксис записи автоматически определяет функцию parse :: Parser a -> (String -> Maybe (a,String)), которая извлекает функцию String -> Maybe (a,String) из файла Parser a. Надеюсь, остальное должно быть очевидно: благодаря каррированию parse можно использовать с двумя аргументами, а не с одним, и это просто приводит к запуску функции, хранящейся в Parser a. Другими словами, ваше определение точно эквивалентно следующему коду:

newtype Parser a = Parser (String -> Maybe (a,String))

parse :: Parser a -> (String -> Maybe (a,String))
parse (Parser p) = p
person bradrn    schedule 19.02.2020