我目前正在阅读关于在Haskell http://www.cs.nott.ac.uk/~pszgmh/monparsing.pdf中解析组合子的Hutton 's and Meijer'论文 . 为此,我试图在scala中实现它们 . 我想构建一些易于编码,扩展且简单而优雅的东西 . 我已经为以下haskell代码提出了两个解决方案
/* Haskell Code */
type Parser a = String -> [(a,String)]
result :: a -> Parser a
result v = \inp -> [(v,inp)]
zero :: Parser a
zero = \inp -> []
item :: Parser Char
item = \inp -> case inp of
[] -> []
(x:xs) -> [(x,xs)]
/* Scala Code */
object Hutton1 {
type Parser[A] = String => List[(A, String)]
def Result[A](v: A): Parser[A] = str => List((v, str))
def Zero[A]: Parser[A] = str => List()
def Character: Parser[Char] = str => if (str.isEmpty) List() else List((str.head, str.tail))
}
object Hutton2 {
trait Parser[A] extends (String => List[(A, String)])
case class Result[A](v: A) extends Parser[A] {
def apply(str: String) = List((v, str))
}
case object Zero extends Parser[T forSome {type T}] {
def apply(str: String) = List()
}
case object Character extends Parser[Char] {
def apply(str: String) = if (str.isEmpty) List() else List((str.head, str.tail))
}
}
object Hutton extends App {
object T1 {
import Hutton1._
def run = {
val r: List[(Int, String)] = Zero("test") ++ Result(5)("test")
println(r.map(x => x._1 + 1) == List(6))
println(Character("abc") == List(('a', "bc")))
}
}
object T2 {
import Hutton2._
def run = {
val r: List[(Int, String)] = Zero("test") ++ Result(5)("test")
println(r.map(x => x._1 + 1) == List(6))
println(Character("abc") == List(('a', "bc")))
}
}
T1.run
T2.run
}
问题1
在Haskell中,零是一个可以按原样使用的函数值,它将解析所有失败的解析器,无论它们是Parser [Int]还是Parser [String] . 在scala中,我们通过调用函数Zero(第一种方法)来实现相同的功能,但是这样我相信每次调用Zero时我只生成一个不同的函数 . 这个陈述是真的吗?有没有办法缓解这种情况?
问题2
在第二种方法中,Zero case对象使用存在性 types Parser[T forSome {type T}]
来扩展Parser . 如果我用 Parser[_]
替换类型,我得到编译错误
Error:(19, 28) class type required but Hutton2.Parser[_] found
case object Zero extends Parser[_] {
^
我认为这两个表达式是等价的 . 是这样的吗?
问题3
您认为哪种方法可以在优雅和简洁方面表达组合器,从而产生更好的结果?
我使用scala 2.11.8
2 回答
注意:我没有编译它,但我知道问题,可以提出两个解决方案 .
zero
定义为多态值 . 在那种风格中,我建议将解析器定义为不是从函数类型派生的object
,而是作为一个案例类的值:正如@Alec所示,是的,每次都会产生一个新值,因为
def
被编译成一个方法 .Parser
协变 . 然后你可以给zero
一个底部结果类型:这些在某种程度上是相关的;在系统F_ <:,这是Scala使用的基础,类型
_|_
(又名Nothing
)和\/T <: Any. T
的行为相同(这在类型和编程语言中暗示,第28章) . 这里给出的两种可能性是这一事实的结果 .对于存在感,我不太熟悉,但我认为虽然无界
T forSome {type T}
会表现得像Nothing
,但Scala不允许存在类型的存在 .问题1
我认为你是对的,这就是为什么:
Zero1
以下每次使用时打印hello
. 解决方案Zero2
涉及使用val
代替 .问题2
不知道 . 我还是刚刚开始使用Scala . 希望有人回答这个 .
问题3
使用Scala的
for
(因为你可以定义自定义flatMap
和map
),这个特性将更好地发挥作用,结果证明(有点)像Haskell的do
. 以下是您所需要的一切 .当然,要做任何有趣的事情,你需要更多的解析器 . 我将向您推荐一个简单的解析器组合器:
Choice(p1: Parser[A], p2: Parser[A]): Parser[A]
,它会尝试两个解析器 . (并根据我的风格重写现有的解析器) .然后,您可以编写如下内容:
并且调用
parser("xyz")
会给你List((('x','x'),"yz"), (('x','y'),"z"))
.