簡體   English   中英

Haskell列表中的Monad

[英]Monad of list in Haskell

我正在嘗試使它成為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)

但這顯然不起作用,因為bind-function中的函數f是a- a -> M b類型。 因此(map f xs)函數產生M b事物的列表。 它實際上應該列出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.

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"

雖然定義fmap = liftM並不少見,但這在IMO方面有些落后。 如果您首先定義更基本的實例,並在其中包含更多的實例,那么事情往往會變得更加清晰。 我將離開<*> = 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而不是>>=

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中實現解析器的材料。 毫無疑問,請看一些已建立的庫是如何做到的, 例如parsec

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM