Написание экземпляров классов типов для вложенных классов в Scala

В недавнем вопросе о переполнении стека автор хотел изменить список синтаксических анализаторов некоторого типа на синтаксический анализатор, который возвращает списки этого типа. Мы можем представить это с помощью последовательности Скалаза для аппликативных функторов:

import scala.util.parsing.combinator._

import scalaz._
import Scalaz._

object parser extends RegexParsers {
  val parsers = List(1, 2, 3).map(repN(_, """\d+""".r))
  def apply(s: String) = parseAll(parsers.sequence, s)
}

Здесь мы берем список из трех синтаксических анализаторов, которые возвращают списки целых чисел, и превращаем его в синтаксический анализатор, который возвращает списки списков целых чисел. К сожалению, Scalaz не предоставляет экземпляр Applicative для Parser , поэтому этот код не компилируется, но это легко исправить:

import scala.util.parsing.combinator._

import scalaz._
import Scalaz._

object parser extends RegexParsers {
  val parsers = List(1, 2, 3).map(repN(_, """\d+""".r))
  def apply(s: String) = parseAll(parsers.sequence, s)

  implicit def ParserPure: Pure[Parser] = new Pure[Parser] {
    def pure[A](a: => A) = success(a)
  }

  implicit def ParserFunctor: Functor[Parser] = new Functor[Parser] {
    def fmap[A, B](p: Parser[A], f: A => B) = p.map(f)
  }

  implicit def ParserBind: Bind[Parser] = new Bind[Parser] {
    def bind[A, B](p: Parser[A], f: A => Parser[B]) = p.flatMap(f)
  }
}

Это работает, как ожидалось: parser ("1 2 3 4 5 6") дает нам, например, Список (Список (1), Список (2, 3), Список (4, 5, 6)) .

(Я знаю, что могу просто указать экземпляр Apply , но экземпляр Bind более краток.) ​​

Было бы неплохо не делать это каждый раз мы расширяем Parsers , но я не понимаю, как получить экземпляр Applicative для Parsers # Parser в более общем плане. Следующий наивный подход, конечно, не работает, так как нам нужно, чтобы экземпляры Parsers были одинаковыми:

implicit def ParserBind: Bind[Parsers#Parser] = new Bind[Parsers#Parser] {
  def bind[A, B](p: Parsers#Parser[A], f: A => Parsers#Parser[B]) = p.flatMap(f)
}

Мне довольно ясно, что это должно быть возможно, но мне не достаточно комфортно с системой типов Scala, чтобы знать, как это сделать.Есть ли что-то простое, чего мне не хватает?


В ответ на ответы ниже: Я попробовал маршрут -Ydependent-method-types и зашел так далеко:

implicit def ParserApplicative(g: Parsers): Applicative[g.Parser] = {
  val f = new Functor[g.Parser] {
    def fmap[A, B](parser: g.Parser[A], f: A => B) = parser.map(f)
  }

  val b = new Bind[g.Parser] {
    def bind[A, B](p: g.Parser[A], f: A => g.Parser[B]) = p.flatMap(f)
  }

  val p = new Pure[g.Parser] {
    def pure[A](a: => A) = g.success(a)
  }

  Applicative.applicative[g.Parser](p, FunctorBindApply[g.Parser](f, b))
}

Проблема (как Didierd ) заключается в том, что неясно, как заставить неявное задействовать. Таким образом, этот подход действительно работает, но вы должны добавить что-то вроде следующего в вашу грамматику:

implicit val applicative = ParserApplicative(this)

На этом этапе подход миксинов, очевидно, намного более привлекателен.

(В качестве примечания: я ожидал, что смогу написать просто Applicative.applicative [g.Parser] выше, но это выдает ошибку о том, что компилятор не может найти неявное значение для Pure [g.Parser] - даже если один сидит прямо рядом с ним. Так что очевидно, что имплициты работают по-другому для зависимых типов методов.)


Благодаря ретрониму ] за то, что указал на трюк, который выполняет то, что я хочу здесь. Я извлек из его кода следующее:

implicit def parserMonad[G <: Parsers with Singleton] =
  new Monad[({ type L[T] = G#Parser[T] })#L] {
    def pure[A](a: => A): G#Parser[A] = {
      object dummy extends Parsers
      dummy.success(a).asInstanceOf[G#Parser[A]]
    }

    def bind[A, B](p: G#Parser[A], f: (A) => G#Parser[B]): G#Parser[B] =
      p.flatMap(f)
  }

Если у вас есть это в области видимости, вы получите экземпляр монады для Parser в любом объекте, расширяющем Parsers . Это своего рода обман из-за актерского состава, но все же довольно аккуратный.

9
задан Community 23 May 2017 в 02:27
поделиться