1

我尝试在 Graham Hutton 的“在 Haskell 中编程”中运行第 8 章中关于功能解析器的片段,无论是 inghci还是frege-repl. 我无法使用do语法对解析器进行排序。我在 Frege 中有以下定义(Haskell 版本仅与item不打包和解包的更简单定义不同,String并且Char与书中相同):

module Parser where
type Parser a = String -> [(a, String)]   

return :: a -> Parser a
return v = \inp -> [(v, inp)]

-- this is Frege version
item :: Parser Char
item = \inp ->
  let inp' = unpacked inp
  in
    case inp' of
        [] -> []
        (x:xs) -> [(x,packed xs)]


parse :: Parser a -> String -> [(a, String)]
parse p inp = p inp

-- sequencing
(>>=) :: Parser a -> (a -> Parser b) -> Parser b
p >>= f = \inp -> case (parse p inp) of
  [] -> []
  [(v,out)] -> parse (f v) out

p :: Parser (Char, Char)
p = do x <- Parser.item
       Parser.item
       y <- Parser.item
       Parser.return (x,y)

-- this works
p' :: Parser (Char, Char)
p' = item Parser.>>= \x ->
     item Parser.>>= \_ ->
     item Parser.>>= \y ->
     Parser.return (x,y)

p'适用于ghcifrege-repl。但是,在尝试加载模块时,我收到了这些消息。首先来自ghci

src/Parser.hs:38:8:
    Couldn't match type ‘[(Char, String)]’ with ‘Char’
    Expected type: String -> [((Char, Char), String)]
      Actual type: Parser ([(Char, String)], [(Char, String)])
    In a stmt of a 'do' block: Parser.return (x, y)
    In the expression:
      do { x <- item;
           item;
           y <- item;
           Parser.return (x, y) }
Failed, modules loaded: none.

frege-repl甚至更不友好,因为它只是用错误堆栈跟踪将我从 repl 中踢了出来:

 Exception in thread "main" frege.runtime.Undefined: returnTypeN: too many arguments
    at frege.prelude.PreludeBase.error(PreludeBase.java:18011)
    at frege.compiler.Utilities.returnTypeN(Utilities.java:1937)
    at frege.compiler.Utilities.returnTypeN(Utilities.java:1928)
    at frege.compiler.GenJava7$80.eval(GenJava7.java:11387)
    at frege.compiler.GenJava7$80.eval(GenJava7.java:11327)
    at frege.runtime.Fun1$1.eval(Fun1.java:63)
    at frege.runtime.Delayed.call(Delayed.java:198)
    at frege.runtime.Delayed.forced(Delayed.java:267)
    at frege.compiler.GenJava7$78.eval(GenJava7.java:11275)
    at frege.compiler.GenJava7$78.eval(GenJava7.java:11272)
    at frege.runtime.Fun1$1.eval(Fun1.java:63)
    at frege.runtime.Delayed.call(Delayed.java:200)
    at frege.runtime.Delayed.forced(Delayed.java:267)
    at frege.control.monad.State$IMonad_State$4.eval(State.java:1900)
    at frege.control.monad.State$IMonad_State$4.eval(State.java:1897)
    at frege.runtime.Fun1$1.eval(Fun1.java:63)
    at frege.runtime.Delayed.call(Delayed.java:198)
    at frege.runtime.Delayed.forced(Delayed.java:267)
    at frege.control.monad.State$IMonad_State$4.eval
...

我的直觉是我需要一些不同的东西>>=return或者我应该告诉编译器一些东西。或者也许我需要将p定义放入Statemonad?

4

2 回答 2

4

这是因为String -> a是在您的do符号中使用的 monad,因为 Prelude 中的实例之一Monad是函数箭头。

因此,例如,xinx <- Parser.item是类型的参数[(Char, String)]

您可以通过创建Parser一个newtype并为其定义您自己的自定义Monad实例来解决此问题。

于 2016-03-14T23:59:17.030 回答
1

以下适用于 Frege(并且应该与 GHC 语言扩展以相同的方式工作RebindableSyntax):

module P
    where


type Parser a = String -> [(a, String)]   

return :: a -> Parser a
return v = \inp -> [(v, inp)]

-- this is Frege version
item :: Parser Char
item  = maybeToList . uncons


parse :: Parser a -> String -> [(a, String)]
parse p inp = p inp

-- sequencing
(>>=) :: Parser a -> (a -> Parser b) -> Parser b
p >>= f = \inp -> case (parse p inp) of
  [] -> []
  [(v,out)] -> parse (f v) out

p :: Parser (Char, Char)
p = do 
    x <- item
    item
    y <- item
    return (x,y)

main = println (p "Frege is cool")

它打印:

[(('F', 'r'), "ege is cool")]

您的版本的主要区别是更高效的item功能,但是,正如我之前所说,这不是堆栈跟踪的原因。您的代码中的 do 存在这个小缩进问题。

所以是的,你可以在这里使用 do 表示法,尽管有些人会称之为“滥用”。

于 2016-03-15T17:52:11.183 回答