简体   繁体   English

Haskell列表中的Monad

[英]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

a simple example 一个简单的例子

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM