假设Parser x
是解析x
的解析器。这个解析器可能拥有一个many
组合子,可以解析零次或多次出现的事情(当项目解析器失败时停止)。
如果Parser
形成一个monad,我可以看到如何实现。如果Parser
只是一个Applicative Functor,我无法弄清楚如何做到这一点。似乎没有任何方法可以检查以前的结果并决定下一步该做什么(正是monads添加的概念)。我错过了什么?
答案 0 :(得分:5)
Alternative
类型类提供many
组合子:
class Applicative f => Alternative f where
empty :: f a
(<|>) :: f a -> f a -> f a
many :: f a -> f [a]
some :: f a -> f [a]
some = some'
many = many'
many' a = some' a <|> pure []
some' a = (:) <$> a <*> many' a
many a
组合子的意思是“零或更多”a
。some a
组合子的意思是“一个或多个”a
。因此:
some a
组合器返回一个a
后跟many a
的列表(即1 + (0 or more)
)。many a
组合器返回some a
或空列表(即(1 or more) | 0
)。 many
组合器取决于(<|>)
运算符,它可以被视为JavaScript等语言的默认运算符。例如,请考虑Alternative
的{{1}}实例:
Maybe
基本上instance Alternative Maybe where
empty = Nothing
Nothing <|> r = r
l <|> _ = l
应该返回左侧值,如果它的真实性。否则它应该返回右侧值。
(<|>)
是一个与Parser
类似定义的数据结构(applicative lexer combinators的概念和解析器组合基本相同):
Maybe
如果解析失败,则返回data Lexer a = Fail | Ok (Maybe a) (Vec (Lexer a))
值。否则返回Fail
值。由于Ok
为Fail <|> pure []
,因此pure []
组合子知道何时停止并返回空列表。
答案 1 :(得分:3)
只能使用Applicative
提供的内容来完成。但是Alternative
有一项功能可以为您提供超出Applicative
的能力:
(<|>) :: f a -> f a -> f a
此功能可让您“结合”#34;两个Alternatives
对a
没有任何限制。但是怎么样?特定仿函数f
固有的东西必须为你提供一种方法。
通常,Alternative
需要一些失败或空虚的概念。就像解析器一样,(<|>)
意味着&#34;尝试解析它,如果失败,请尝试另外一件事#34;。但这种依赖于以前的价值&#34; 隐藏在实施(<|>)
的机制中。它可用于外部接口。
从(<|>)
,可以实现零或一组合:
optional :: Alternative f => f a -> f (Maybe a)
optional v = Just <$> v <|> pure Nothing
some
和many
的定义相似,但它们需要相互递归的函数。
请注意,Applicative
不是Alternative
。例如,您无法将Identity
仿函数设为Alternative
。您将如何实施empty
?
答案 2 :(得分:1)
many
是Alternative
类(link)的一种类方法,它表明一般应用仿函数并不总是many
实现。