来自ghc/libraries/base/GHC/Base.hs 有这个递归声明:
... :: f a -> f [a]
...
many_v = some_v <|> pure []
some_v = liftA2 (:) v many_v -- (:) <$> v <*> many_v
参数v 必须是一个类型的值,它是
Alternative(和Applicative 和Functor)。
v 已解除,只需要应用 (<*>)。
应用程序会生成一个提升列表。
some 和 many 递归应用 v 的构造函数,
并将构造的值放入列表中。
-
some 停止应用程序,当第一个 empty 被构造时
-
many 在此之外继续申请
[] 是Alternative 的实例:
instance Alternative [] where
empty = []
(<|>) = (++)
some 尝试使用列表:
av = [[2], [2,3], [], [2,3,5]]
some av -- no error, but never stops
do {v <- some av; return v;} -- no error, but never stops
与letter 相比
What are Alternative's "some" and "many" useful for?:
import Control.Monad(Functor(..))
import Control.Applicative
import Data.Char
newtype P a = P { runP :: String -> [(a,String)] }
instance Functor P where
fmap f (P q) = P (\s -> [ (f y,ys) | (y,ys) <- q s])
instance Applicative P where
pure x = P (\s -> [(x,s)])
P p <*> P q = P (\s -> [(x y, ys) | (x,xs) <- p s, (y,ys) <- q xs])
letter = P p where
p (x:xs) | isAlpha x = [(x,xs)]
p _ = []
instance Alternative P where
P p <|> P q = P (\s-> p s ++ q s)
empty = P (\s-> [])
有用法:
runP (many letter) "ab123"
letter 是一个智能构造函数,即
使用局部变量 p 构造带有字段 runP 的值 P。
(访问器的结果)runP 是一个函数。
P 是实现 Alternative 的类型以及构造函数。
P 代表解析器。
fmap 不用于some 和many。
<*> 导致函数runP 的应用,
谁的论点还没有出现。
基本上some 和many 构造了一个程序,它会从它的参数中吃掉。
参数必须是一个列表。
由于惰性,递归在第一个构造函数处停止。
p = some letter -- constructs a program accessed via @runP@
runP p "a1" -- [("a","1")]
q = some [2] -- basically also a program
q -- never stops
递归应用不是empty([]为列表),
因为没有停止标准的来源,即没有输入。
这些停止:
some [] -- []
many [] -- [[]]
some Nothing -- Nothing
many Nothing -- Just []
对some和many(v)的参数有更多要求。
-
v 是 Alternative 的实例类型的值。
-
v 类型的构造函数的递归应用必须以empty 停止。
-
v 必须包含在构造期间应用的函数,<*> 具有停止条件。
结论:
some 和 many 不能应用于列表值,
即使列表实现了Alternative。
list为什么要实现Alternative?
我想,就是在Applicative之上加上Monoid接口<|>和empty,
不是因为some 和many。
foldr (<|>) [] [[2],[],[3,4]] -- [2,3,4]
some 和 many 似乎只是用于解析器构造
或更一般地,构建消耗输入的程序
并产生更多的输出,其中一些可以是empty。
这已经很笼统了。
但是Alternative中的地方只是有道理的,
如果大多数Alternative 实例都有合理的用法。
事实并非如此。