【问题标题】:Monad of list in HaskellHaskell中列表的单子
【发布时间】:2016-03-29 08:40:19
【问题描述】:

我正在尝试在 Haskell 中将此作为 Monad 的实例:

data Parser a = Done ([a], String) | Fail String

现在我尝试使用这段代码使其成为 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)

但这显然不起作用,因为绑定函数中的函数fa -&gt; M b 类型。所以(map f xs) 函数会生成一个M b-things 列表。它实际上应该列出b 的列表。我怎样才能在 Haskell 中做到这一点?

注意: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.

【问题讨论】:

  • 在你map f xs 之后,检查结果列表:要么你有一些Fails,要么你没有。在前者中,您想返回Fail,我猜是(?)。在后者中,您可以访问 Done 构造函数中的对列表。
  • 当列表中的一个元素是失败时,我确实想返回失败,但是我如何访问构造函数中的对列表呢?我真的不知道该怎么做。
  • 这里的a 是什么——这是解析器的结果吗? String 是什么?通常你有一个返回值和一个带有剩余/未解析字符串的返回值,解析器是一个接受输入字符串并返回的函数 - 例如 - 一些结果和剩余字符串的元组

标签: list haskell monads


【解决方案1】:

leftaroundabout 已经向您展示了一些问题。

通常我希望解析器是某种函数,它接受输入-String,可能会使用其中的一些字符串,然后将结果与未使用的输入一起返回。

基于这个想法,你可以扩展你的代码来做到这一点:

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

一个简单的例子

这是一个可以接受任何字符的简单解析器:

parseAnyChar :: Parser Char
parseAnyChar = Parser f
  where f (c:xs) = Done (c, xs)
        f ""     = Fail "nothing to parse"

这就是你将如何使用它:

λ> run parseAnyChar "Hello"
Done ('H',"ello")
λ> run parseAnyChar ""
Fail "nothing to parse"

【讨论】:

  • 现在我明白了,以这种方式定义解析器更有意义。非常感谢:)
【解决方案2】:

虽然定义fmap = liftM 等并不少见,但这有点倒退了IMO。如果您首先定义更基本的实例并将更多涉及的实例基于它们,事情通常会变得更清晰。我会离开&lt;*&gt; = 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

现在fmap 已经存在,我可以用“更数学”的方式定义Monad:定义join 而不是&gt;&gt;=

instance Monad Parser where
  return = pure
  q >>= f = joinParser $ fmap f q

这意味着您将使用直观可处理的具体值,而不必担心通过解析器线程化函数。因此,您可以很清楚地看到发生了什么,只需写出递归:

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) = ??

此时您可以清楚地看到 Carsten 已经说过的内容:您的 Parser 类型作为解析器并没有真正意义。内部和外部 Done 包装器都有 rest 数据;结合它意味着你结合了未完成的工作......这不是解析器所做的。

在网上搜索一下,有很多关于如何在 Haskell 中实现解析器的资料。有疑问,看看一些已建立的图书馆是如何做到的,e.g. parsec

【讨论】:

  • 感谢您的澄清回答。真正定义 Functor 和 Parser 确实更清晰。
猜你喜欢
  • 1970-01-01
  • 2018-01-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-21
  • 1970-01-01
相关资源
最近更新 更多