7

我和我的一个高中生将尝试将 Haskell 的 Parsec 解析器组合库移植到 Scala 中。(与 Scala 的内置解析库相比,它的优势在于您可以相当轻松地传递状态,因为所有解析器都是单子。)

我遇到的第一个障碍是试图弄清楚 Functor 在 scalaz 中是如何工作的。有人可以解释如何转换这个 Haskell 代码:

data Reply s u a = Ok a !(State s u) ParseError
                 | Error ParseError


instance Functor (Reply s u) where
    fmap f (Ok x s e) = Ok (f x) s e
    fmap _ (Error e) = Error e -- XXX

进入 Scala(我假设使用 Scalaz)。我做到了

sealed abstract class Reply[S, U, A]
case class Ok[S, U, A](a: A, state: State[S, U], error: ParseError)
    extends Reply[S, U, A]
case class Error[S, U, A](error: ParseError) extends Reply[S, U, A]

并且知道我应该Reply扩展这个scalaz.Functor特征,但我不知道该怎么做。(大多数情况下,我无法弄清楚F[_]参数的作用。)

任何帮助表示赞赏!

谢谢,托德

根据 dflemstr 的回答,我想出了这个:

sealed abstract class Reply[S, U, A]
object Reply {
  implicit def ReplyFunctor[S, U]  = {
    type ReplySU[A] = Reply[S, U, A]
    new Functor[ReplySU] {
      def fmap[A, B](r: ReplySU[A], f: A => B) = r match {
        case Ok(a, state, error) => Ok(f(a), state, error)
        case Error(error) => Error[S, U, B](error)
      }
    }
  }
}
case class Ok[S, U, A](a: A, state: State[S, U], error: ParseError) 
    extends Reply[S, U, A]()
case class Error[S, U, A](error: ParseError) extends Reply[S, U, A]()

我不确定的是那种ReplySU[A]类型。FunctorHaskell 中的实际是Reply s u咖喱类型和a缺少类型。这是我应该如何在 Scala 中做同样的事情,还是我过于复杂了?

4

2 回答 2

10

Functor[F[_]]中,F意味着它是一个类型构造函数,也就是一个参数化的类型,它必须将一些其他类型作为参数才能成为一个完全限定的类型。例如, if Fis List, thenList[Int]是该参数化类型的类型实例。

所以当你定义一个 type 的值时Functor[List],就意味着它是一个描述Lists 的函子性质的对象,并且函子对象会使用高阶类型List来构造各种类型实例,例如List[A]and List[B]

此外,您必须了解 Scala 的类和 Haskell 的类之间的区别。Haskell 中的类实例最好由 Scala 中的隐式值建模,而不是接口的实现;虽然在 Java/Scala 中您需要一个对象的实例来拥有一个接口的实例,但您可以在 Haskell 中拥有一个类的实例,而无需该类处理的值类型的实例。

例如,想象一下如何Read在 Scala 中实现 Haskell 中的类。该类Read反序列化字符串中的值;有一个read用 type调用的函数,Read a => String -> a因此对于任何a具有Read实例的类型,您都可以将 aString转换为 type 的实例a。如果您使用 Scala 中的接口对其进行建模,例如class Foo implements Read[Foo],那么您将如何将字符串转换为 的实例Foo?你不能打电话Foo.read,因为你还Foo没有;该read函数应该返回一个!

相反,您创建一个单独的ReadFoo: Read[Foo]对象,其中包含Readtype的函数的实现Foo。然后,您可以安全地调用ReadFoo.read(string: String): Foo并获得Foo返回类型。

对于仿函数实例,您需要以下内容:

// All implicits in the companion object of Reply are automatically brought into
// scope when Reply is imported
object Reply {
  // Describes how to treat a `Reply` as a functor
  implicit val ReplyFunctor: Functor[Reply] = new Functor[Reply] {
    def fmap[A, B](r: Reply[A], f: A => B) = r match {
      case Ok(x, s, e) => Ok(f(x), s, e)
      case err         => err // leave value unchanged
    }
  }
}
于 2012-03-30T10:41:46.743 回答
2

啊,你的意思是这样的端口: https ://github.com/runarorama/scarpia ?一般来说,这方面的使用不多(可悲): https ://wiki.scala-lang.org/display/SW/Tools+and+Libraries

于 2012-03-30T13:28:07.607 回答