Trong this recent Stack Overflow question, tác giả muốn thay đổi danh sách các trình phân tích cú pháp của một số loại thành một trình phân tích cú pháp trả về các danh sách thuộc loại đó. Chúng ta có thể tưởng tượng làm điều này với Scalaz của sequence
cho functors applicative:Viết thể loại lớp cho các lớp lồng nhau trong 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)
}
Ở đây chúng ta phải mất một danh sách ba parsers rằng trở lại danh sách các số nguyên và biến nó thành một phân tích cú pháp trả về danh sách liệt kê các số nguyên. Thật không may Scalaz không cung cấp một ví dụ Applicative
cho Parser
, do đó, mã này không biên dịch, nhưng đó là dễ dàng để sửa chữa:
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)
}
}
này hoạt động như mong đợi: parser("1 2 3 4 5 6")
cho chúng ta List(List(1), List(2, 3), List(4, 5, 6))
, ví dụ.
(Tôi biết tôi chỉ có thể đưa ra một ví dụ Apply
, nhưng trường hợp Bind
ngắn gọn hơn.)
Nó sẽ được tốt đẹp để không phải làm điều này mỗi khi chúng tôi mở rộng Parsers
, nhưng tôi không rõ ràng về cách nhận được phiên bản Applicative
cho số Parsers#Parser
thường hơn. Cách tiếp cận ngây thơ sau đây tất nhiên không làm việc, kể từ khi chúng ta cần các trường hợp Parsers
là giống nhau:
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)
}
Nó khá rõ ràng với tôi rằng điều này nên có thể, nhưng tôi không cảm thấy thoải mái đủ với Scala của loại hệ thống để biết làm thế nào để đi về nó. Có cái gì đơn giản mà tôi đang thiếu?
Để đối phó với các câu trả lời dưới đây: Tôi đã thử các tuyến đường -Ydependent-method-types
, và nhận được điều này cho đến nay:
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))
}
Vấn đề (như didierd chỉ ra) là nó không rõ ràng như thế nào để có được những implicit
để bắt đầu. Cách tiếp cận này hoạt động, nhưng bạn phải thêm một số thông tin như sau vào ngữ pháp của mình:
implicit val applicative = ParserApplicative(this)
Tại thời điểm đó, trộn trong cách tiếp cận rõ ràng là hấp dẫn hơn nhiều.
(Lưu ý: Tôi dự kiến có thể viết đơn giản là Applicative.applicative[g.Parser]
ở trên, nhưng điều đó cho biết lỗi trình biên dịch không thể tìm thấy giá trị tiềm ẩn cho Pure[g.Parser]
— mặc dù một người đang ngồi ngay bên cạnh nó. vì vậy, rõ ràng có điều gì đó khác nhau về cách thức implicits làm việc với nhiều loại phương pháp phụ thuộc.)
Nhờ retronym để chỉ ra một thủ thuật mà hoàn thành những gì tôi muốn ở đây. Tôi đã tóm tắt như sau từ his code:
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)
}
Nếu bạn có điều này trong phạm vi, bạn sẽ có được một trường hợp đơn nguyên cho Parser
ở bất kỳ đối tượng mở rộng Parsers
. Đó là loại gian lận vì dàn diễn viên, nhưng vẫn khá gọn gàng.
Đây là thông minh và đẹp hơn nhiều so với phụ thuộc phiên bản loại phương pháp của tôi, nhưng tôi vẫn muốn làm mà không có 'ngầm val M: Monad [Parser] = parserMonad (testParser) ' . Bạn có nghĩ rằng điều đó là không thể? –
Tôi cần ví dụ về 'Trình phân tích cú pháp' để gọi' thành công'. Bạn có thể làm cho chính 'trình phân tích cú pháp' ẩn chứa nó để cung cấp cho nó' parserMonad', nhưng điều đó không có vẻ giống như một ý tưởng hay. – retronym
Nếu bạn sẵn sàng thừa nhận một 'asInstanceOf', bạn thực sự có thể thực hiện việc này: https://github.com/retronym/scalaz7-experimental/commit/aa80e4792799a509c728eecff771ec74518720e7 – retronym