【问题标题】:how to translate Haskell into Scalaz?如何将 Haskell 翻译成 Scalaz?
【发布时间】:2012-03-30 10:25:02
【问题描述】:

我和我的一个高中生打算尝试将 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,带有咖喱类型,而a 类型缺失。这是我应该如何在 Scala 中做同样的事情,还是我过于复杂了?

【问题讨论】:

  • Scala 解析器也是单子! Scala monad 是任何具有 mapflatMap 方法实现的对象(只要它们满足 monad 公理)。
  • 看起来您已经得到了答案,但对于未来,请随时加入。irc://freenode.net/#scalaz

标签: scala haskell functor scalaz


【解决方案1】:

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

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

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

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

相反,您创建一个单独的ReadFoo: Read[Foo] 对象,其中包含Read 类型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
    }
  }
}

【讨论】:

  • 谢谢!在我接受答案之前,我今天要稍微摆弄一下(以防万一我需要澄清)。
  • 你能看看我上面的编辑以确保我在正确的轨道上吗?
  • 我认为你可以将ReplySU类型替换为scalaz.PartialApply2Of3[Reply, S, U]#Apply。然后您也不必使用新的专用类型创建该内部块。另外,我似乎完全忘记了Reply 在我的回答中使用了多个类型参数,抱歉!
  • 也可以看how some of the standard functors are implemented;他们在这种情况下使用匿名类型函数(例如参见 Tuple2)。
  • 谢谢。 PartialApply 正是我所需要的!
【解决方案2】:

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

【讨论】:

  • 是的,不幸的是,看起来有人开始使用 Scarpia,但从未完成。
  • @TOB FWIW,Rúnar Bjarnason,又名 apocalisp,是 Scalaz 的主要贡献者之一,iirc 也是 Scala 的 Packrat Parsers 的作者之一。
猜你喜欢
  • 1970-01-01
  • 2012-07-05
  • 1970-01-01
  • 2011-09-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-01
  • 1970-01-01
相关资源
最近更新 更多