如何表达功能类型?

时间:2016-06-10 08:53:53

标签: scala haskell functional-programming existential-type

我目前正在阅读Hutton's和Meijer关于在Haskell http://www.cs.nott.ac.uk/~pszgmh/monparsing.pdf中解析组合子的论文。为此,我试图在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

在第二种方法中,零案例对象使用存在性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 个答案:

答案 0 :(得分:2)

注意:我没有编译它,但我知道问题,可以提出两个解决方案。

  1. 更多Haskellish方法是不使用子类型,而是将zero定义为多态值。在那种风格中,我建议将解析器定义为不是object来自函数类型,而是作为一个案例类的值:

    final case class Parser[T](run: String => List[(T, String)])
    def zero[T]: Parser[T] = Parser(...)
    

    如@Alec所示,是的,每次都会生成一个新值,因为def被编译为方法。

  2. 如果您想使用子类型,则需要使Parser协变。然后,您可以为zero提供最终结果类型:

    trait Parser[+A] extends (String => List[(A, String)])
    case object Zero extends Parser[Nothing] {...}
    
  3. 这些在某种程度上是相关的;在系统F_&lt;:,这是Scala使用的基础,类型_|_(又名Nothing)和\/T <: Any. T的行为相同(这在类型和编程中暗示语言,第28章)。这里给出的两种可能性是这一事实的结果。

    对于我不熟悉的存在感,但我认为虽然无界T forSome {type T}的行为类似于Nothing,但Scala不允许存在类型的存在。

答案 1 :(得分:1)

问题1

我认为你是对的,原因如下:Zero1下面每次使用时都会打印hello。解决方案Zero2涉及使用val代替。

def Zero1[A]: Parser[A] = { println("hi"); str => List() }
val Zero2: Parser[Nothing] = str => List()

问题2

不知道。我还是刚刚开始使用Scala。希望有人回答这个问题。

问题3

使用Scala的for(因为您可以定义自定义flatMapmap),这个特性将更好地发挥作用,结果证明(有点)像Haskell的do 。以下是您所需要的一切。

trait Parser[A] extends (String => List[(A, String)]) {
  def flatMap[B](f: A => Parser[B]): Parser[B] = {
      val p1 = this
      new Parser[B] {
        def apply(s1: String) = for {
          (a,s2) <- p1(s1)
          p2 = f(a)
          (b,s3) <- p2(s2)
      } yield (b,s3)
    }
  }

  def map[B](f: A => B): Parser[B] = {
    val p = this
    new Parser[B] {
      def apply(s1: String) = for ((a,s2) <- p(s1)) yield (f(a),s2) 
    }
  }
}

当然,要做任何有趣的事情,你需要更多的解析器。我将向您推荐一个简单的解析器组合器:Choice(p1: Parser[A], p2: Parser[A]): Parser[A],它会尝试两个解析器。 (并根据我的风格重写现有的解析器。)

def choice[A](p1: Parser[A], p2: Parser[A]): Parser[A] = new Parser[A] {
  def apply(s: String): List[(A,String)] = { p1(s) ++ p2(s) }
}

def unit[A](x: A): Parser[A] = new Parser[A] {
  def apply(s: String): List[(A,String)] = List((x,s))
}

val character: Parser[Char] = new Parser[Char] {
  def apply(s: String): List[(Char,String)] = List((s.head,s.tail))
}

然后,您可以编写如下内容:

val parser: Parser[(Char,Char)] = for {
  x <- choice(unit('x'),char)
  y <- char
} yield (x,y)

致电parser("xyz")会为您提供List((('x','x'),"yz"), (('x','y'),"z"))

相关问题