Jaskell 的一次大升级

博客介绍了JaskellCore库的大版本升级,将内置组合子作为扩展,通过typeclasses或者Scala3的extension语法实现更面向对象的风格。这一改变使得代码更易读、更简洁,尤其在多层组合的情况下。同时,JaskellDotty也已升级,支持相同的功能。这一优化在保持与自定义组合子一致性的同时,提升了解析器的可维护性和可读性。
摘要由CSDN通过智能技术生成

最近的工作经常要写 parser,也就有了改进的目标,这几天对 Jaskell Core 做了一个大版本升级,主要是将内置组合子作为扩展,一次打包封装为 typeclasses 。
例如,以前这样定义的 parser:

  "Brackets" should "test brackets pairs" in {

    val state = State("[hello]");
    val parser = Between(
      ch('['),
      ch(']'),
      many(Ne(']'))
    )

    val re = parser(state).map(_.mkString)
    re should be (Success("hello"))
  }


 现在可以写为更面向对象的风格:

  "Combinator" should "test typeclasses style" in {
    import jaskell.parsec.Combinator.BuiltIn
    val state = State("[hello]");
    val parser = nch(']').many.between(ch('['), ch(']'))

    val re = parser(state).map(_.mkString)
    re should be (Success("hello"))
  }


这对于一些需要多层组合的代码,是个有效的优化,例如我们经常要在一些解释器里写类似 `attempt((prefix), attempt(parser))` 这样的代码,现在可以写成 `do(prefix.attempt, parser.attempt)` 。
在 scala 中,这样的支持不会导致内置组合子和自定义组合子的“不平等”,因为它并不是侵入式的在 Parsec trait 中加入新方法,而是通过 implicit class 扩展实现:

object Combinator {
  //...
  implicit class BuiltIn[E, T](p: Parsec[E, T]) {
    def attempt: Parsec[E, T] = Attempt(p)

    def ahead: Parsec[E, T] = Ahead(p)

    def or(other: Parsec[E, T]): Parsec[E, T] = Attempt(p) <|> other

    def many: Parsec[E, Seq[T]] = Many(p)

    def many1: Parsec[E, Seq[T]] = Many1(p)

    def manyTill(end: Parsec[E, _]): Parsec[E, Seq[T]] = ManyTill(p, end)

    def skip: Parsec[E, Unit] = Skip(p)

    def skip1: Parsec[E, Unit] = Skip1(p)

    def sepBy(by: Parsec[E, _]): Parsec[E, Seq[T]] = SepBy(p, by)

    def sepBy1(by: Parsec[E, _]): Parsec[E, Seq[T]] = SepBy1(p, by)

    def find: Parsec[E, T] = Find(p)

    def between(open: Parsec[E, _], close: Parsec[E, _]): Parsec[E, T] = Between(open, close, p)
  }
}


在需要使用这些扩展的代码中,`import jaskell.parsec.Combinator.BuiltIn ` ,即可。同理,我们也可以根据需要添加新的扩展。

从效果上看,这个扩展使代码更加“面向对象”,但是在实现上,这恰恰是函数式编程的 typeclass 风格。当然,Objective C 和 C Sharp,也早就有类似的语法支持。在 Ruby 中,这种对类型“重定义”的功能,可以通过注入实现。
这个功能在 Jaskell Core 0.7.1 中已经可用。同样,在 Jaskell Dotty 中,我们通过 Scala 3 的extension 语法,也提供了这个能力:


extension [E, T](parsec: Parsec[E, T]) {
  def attempt: Parsec[E, T] = Attempt(parsec)
  def ahead: Parsec[E, T] = Ahead(parsec)
  def or(other: Parsec[E, T]): Parsec[E, T] = Attempt(parsec) <|> other
  def many: Parsec[E, Seq[T]] = Many(parsec)
  def many1: Parsec[E, Seq[T]] = Many1(parsec)
  def manyTill(end: Parsec[E, _]): Parsec[E, Seq[T]] = ManyTill(parsec, end)
  def skip: Parsec[E, Unit] = Skip(parsec)
  def skip1: Parsec[E, Unit] = Skip1(parsec)
  def sepBy(by: Parsec[E, _]): Parsec[E, Seq[T]] = SepBy(parsec, by)
  def sepBy1(by: Parsec[E, _]): Parsec[E, Seq[T]] = SepBy1(parsec, by)
  def find: Parsec[E, T] = Find(parsec)
  def between(open: Parsec[E, _], close: Parsec[E, _]): Parsec[E, T] = Between(open, close, parsec)
}


目前 Jaskell Dotty 已经升级至 0.4.0,支持 scala 3.0.2 和内置组合子扩展。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ccat

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值