如何将Haskell翻译成Scalaz?

时间:2012-03-30 10:25:03

标签: scala haskell functor scalaz

我的一个高中生和我将尝试将一个Haskell的Parsec解析器组合库移植到Scala中。 (它比Scala的内置解析库更有优势,你可以很容易地传递状态,因为所有的解析器都是monad。)

我遇到的第一个障碍是试图弄清楚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]类型。 Haskell中的实际FunctorReply s u,其中包含curried类型且缺少a类型。这是我在Scala中应该做同样的事情还是让事情过于复杂?

2 个答案:

答案 0 :(得分:10)

Functor[F[_]]中,F表示它是类型构造函数,即参数化类型,必须将其他类型作为参数才能成为完全合格的类型。例如,如果FList,则List[Int]是该参数化类型的类型实例。

因此,当您定义类型Functor[List]的值时,这意味着它是描述List s的仿函数性质的对象,并且仿函数对象将使用高阶类型{{ 1}}构建各种类型的实例,例如ListList[A]

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

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

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

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

Foo

答案 1 :(得分:2)

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