Fact "star": in your implementation of (<*>)
:
Parser p1 <*> Parser p2 = ...
...we must compute enough to know that both arguments are actually applications of the Parser
constructor to something before we may proceed to the right-hand side of the equation.
Fact "pipe strict": in this implementation:
Parser p1 <|> Parser p2 = ...
...we must compute enough to know that both parsers are actually applications of the Parser
constructor to something before we may proceed to the right-hand side of the equals sign.
Fact "pipe lazy": in this implementation:
p1 <|> p2 = Parser $ ...
...we may proceed to the right-hand side of the equals sign without doing any computation on p1
or p2
.
This is important, because:
some v = some_v where
some_v = pure (:) <*> v <*> (some_v <|> pure [])
Let's take your first implementation, the one about which we know the "pipe strict" fact. We want to know if some_v
is an application of Parser
to something. Thanks to fact "star", we must therefore know whether pure (:)
, v
, and some_v <|> pure []
are applications of Parser
to something. To know this last one, by fact "pipe strict", we must know whether some_v
and pure []
are applications of Parser
to something. Whoops! We just showed that to know whether some_v
is an application of Parser
to something, we need to know whether some_v
is an application of Parser
to something -- an infinite loop!
On the other hand, with your second implementation, to check whether some_v
is a Parser _
, we still must check pure (:)
, v
, and some_v <|> pure []
, but thanks to fact "pipe lazy", that's all we need to check -- we can be confident that some_v <|> pure []
is a Parser _
without first checking recursively that some_v
and pure []
are.
(And next, you will learn about newtype
-- and be confused yet again when changing from data
to newtype
makes both implementation work!)
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…