Если я использую сопоставление с ленивым шаблоном P f <*> ~(P a) = ...
, то оно работает нормально. Почему?
Этот вопрос обсуждался недавно . Вы также можете исправить это, используя newtype
вместо data
: newtype Parser a = P (Input -> ParseResult a)
. (*)
Определение list1
хочет знать оба аргумента синтаксического анализатора для <*>
но на самом деле, когда первое не удастся (когда ввод исчерпан), нам не нужно знать второе! Но так как мы навязываем его, он навязывает свой второй аргумент, и тот навязывает свой второй синтаксический анализатор до бесконечности. (**) То есть p
потерпит неудачу при исчерпании ввода, но мыиметь list1 p = (:.) <$> p <*> list p
, что заставляет list p
, даже если он не будет работать, если предыдущий p
не удастся. Вот причина бесконечного зацикливания, и почему ваше исправление с ленивым шаблоном работает.
В чем разница между data
и newtype
с точки зрения лени?
(*) newtype
Тип d всегда имеет только один конструктор данных, и сопоставление с образцом для него не на самом деле вызывает значение, поэтому оно неявно похоже на ленивоешаблон. Попробуйте newtype P = P Int
, let foo (P i) = 42 in foo undefined
и убедитесь, что это работает.
(**) Это происходит, когда синтаксический анализатор еще готов, скомпонован;до того, как комбинированный, составной синтаксический анализатор даже запустится на фактическом вводе. Это означает, что есть еще один, третий способ решения проблемы: определить
list1 p = (:.) <$> p <*> P (\s -> parse (list p) s)
Это должно работать независимо от лени <*>
и от того, использовались ли data
или newtype
.
Интересно, что приведенное выше определение означает, что синтаксический анализатор будет фактически создан во время выполнения, в зависимости от входных данных, которые являются определяющей характеристикой Monad, а не Applicative, которая должна быть заранее известна статически. Но разница здесь в том, что Applicative зависит от скрытого состояния ввода, а не от «возвращаемого» значения.