[英]Monad of list in Haskell
I'm trying to make this an instance of Monad in Haskell: 我正在尝试使它成为Haskell中Monad的一个实例:
data Parser a = Done ([a], String) | Fail String
Now I try this code to make it an instance of Monad: 现在,我尝试使用以下代码使其成为Monad的实例:
instance Functor Parser where
fmap = liftM
instance Applicative Parser where
pure = return
(<*>) = ap
instance Monad Parser where
return xs = Done ([], xs)
Done (xs, s) >>= f = Done (concat (map f xs)), s)
But this obviously doesn't work, because the function f
in the bind-function is of the type a -> M b
. 但这显然不起作用,因为bind-function中的函数
f
是a- a -> M b
类型。 So the (map f xs)
function yields a list of M b
-things. 因此
(map f xs)
函数产生M b
事物的列表。 It should actually make a list of b
's. 它实际上应该列出
b
的列表。 How can I do this in Haskell? 如何在Haskell中执行此操作?
NOTE: The actual error given by GHC 7.10.3 is: 注意:GHC 7.10.3给出的实际错误是:
SuperInterpreter.hs:71:27:
Couldn't match expected type `String' with actual type `a'
`a' is a rigid type variable bound by
the type signature for return :: a -> Parser a
at SuperInterpreter.hs:71:5
Relevant bindings include
xs :: a (bound at SuperInterpreter.hs:71:12)
return :: a -> Parser a (bound at SuperInterpreter.hs:71:5)
In the expression: xs
In the first argument of `Done', namely `([], xs)'
SuperInterpreter.hs:72:45:
Couldn't match type `Parser b' with `[b]'
Expected type: a -> [b]
Actual type: a -> Parser b
Relevant bindings include
f :: a -> Parser b (bound at SuperInterpreter.hs:72:22)
(>>=) :: Parser a -> (a -> Parser b) -> Parser b
(bound at SuperInterpreter.hs:72:5)
In the first argument of `map', namely `f'
In the first argument of `concat', namely `(map f xs)'
Failed, modules loaded: none.
leftaroundabout already showed you some of the problems. leftaroundabout已经向您显示了一些问题。
Usually I expect a parser to be some kind of function that takes an input- String
, maybe consuming some of this string and then returning an result together with the unconsumed input. 通常,我希望解析器是一种接受输入
String
的函数,可能会消耗其中的一些字符串,然后将结果与未使用的输入一起返回。
Based on this idea you can extent your code to do just this: 基于这个想法,您可以扩展代码来做到这一点:
data ParserResult a
= Done (a, String)
| Fail String
deriving Show
data Parser a = Parser { run :: String -> ParserResult a }
instance Functor Parser where
fmap = liftM
instance Applicative Parser where
pure = return
(<*>) = ap
instance Monad Parser where
return a = Parser $ \ xs -> Done (a, xs)
p >>= f = Parser $ \ xs ->
case run p xs of
Done (a, xs') -> run (f a) xs'
Fail msg -> Fail msg
here is a simple parser that would accept any character: 这是一个可以接受任何字符的简单解析器:
parseAnyChar :: Parser Char
parseAnyChar = Parser f
where f (c:xs) = Done (c, xs)
f "" = Fail "nothing to parse"
and this is how you would use it: 这是您将如何使用它:
λ> run parseAnyChar "Hello"
Done ('H',"ello")
λ> run parseAnyChar ""
Fail "nothing to parse"
While it's not completely uncommon to define fmap = liftM
and so on, this is a bit backwards IMO. 虽然定义
fmap = liftM
并不少见,但这在IMO方面有些落后。 If you first define the more fundamental instances and base the more involved ones on them, things often come out clearer. 如果您首先定义更基本的实例,并在其中包含更多的实例,那么事情往往会变得更加清晰。 I'll leave
<*> = ap
, but transform everything else: 我将离开
<*> = ap
,但要转换其他所有内容:
instance Functor Parser where -- Note that you can `derive` this automatically
fmap f (Done vs rest) = Done (map f vs) rest
fmap f (Fail err) = Fail err
instance Applicative Parser where
pure xs = Done ([], xs)
(<*>) = ap
Now with fmap
already present, I can define Monad
in the “more mathematical” way: define join
instead of >>=
. 现在已经有了
fmap
,我可以用“更数学的”方式定义Monad
:定义join
而不是>>=
。
instance Monad Parser where
return = pure
q >>= f = joinParser $ fmap f q
That means you'll work with intuitively handleable concrete values, rather than having to worry about threading a function through a parser. 这意味着您将使用直观可处理的具体值,而不必担心通过解析器线程化函数。 You can therefore see quite clearly what's going on, just write out the recursion:
因此,您可以清楚地看到发生了什么,只需写出递归即可:
joinParser :: Parser (Parser a) -> Parser a
joinParser (Fail err) = Fail err
joinParser (Done [] rest) = Done [] rest
joinParser (Done (Fail err : _) _) = Fail err
joinParser (Done (Done v0 rest0 : pss) rest) = ??
at this point you see clearly what Carsten already remarked: your Parser
type doesn't really make sense as a parser. 在这一点上,您可以清楚地看到Carsten已经说过的话:
Parser
类型对于解析器来说实际上没有任何意义。 Both the inner and outer Done
wrappers somehow have rest
data; 内部的和外部的
Done
包装器都以某种方式包含rest
数据; combining it would mean you combine the undone work... this is not what a parser does. 结合起来就意味着您要结合未完成的工作……这不是解析器所做的。
Search the web a bit, there's plenty of material on how to implement parsers in Haskell. 在网上搜索一下,有很多关于如何在Haskell中实现解析器的材料。 In doubt, look how some established library does it, eg parsec .
毫无疑问,请看一些已建立的库是如何做到的, 例如parsec 。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.