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