I have the following type class and classes:
/// A parser combinator.
trait Combinator[T] {
/// The context from which elements are being parsed, typically a stream of tokens.
type Context
/// The element being parsed.
type Element
extension (self: T) {
/// Parses and returns an element from `context`.
def parse(context: Context): Option[Element]
}
}
final case class Apply[C, E](action: C => Option[E])
final case class Combine[A, B](first: A, second: B)
Now my goal is to create type class instances for my two case classes. I managed to create one for Apply
:
given [C, E]: Combinator[Apply[C, E]] with {
type Context = C
type Element = E
extension(self: Apply[C, E]) {
def parse(context: C): Option[E] = self.action(context)
}
}
Sadly I’m really struggling with the type checker when it comes to the second one. I’ve got this far:
given [A, B](using
f: Combinator[A],
s: Combinator[B],
q: f.Context =:= s.Context
): Combinator[Combine[A, B]] with {
type Context = f.Context
type Element = (f.Element, s.Element)
extension(self: Combine[A, B]) {
def parse(context: Context): Option[Element] = ???
}
}
For context, I’m trying to write a mini parser combinator library to learn about type classes in Scala. Apply
is a combinator that just applies a closure and Combine
is one that concatenates the results of two other combinators. So my goal is to say that:
- There is an instance of
Combinator
forCombine[A,B]
where there exist instances ofCombinator
for bothA
andB
- The instance requires that both
A
andB
share the same type memberContext
The code above compiles but it is seemingly unusable. For example:
@main def hello: Unit = {
val source = (0 to 10).toList
val stream = source.to(mutable.ListBuffer)
val n = Apply[mutable.ListBuffer[Int], Int](s => s.popFirst())
val m = Combine[A, A](n, n)
m.parse(stream) // type mismatch, found `mutable.ListBuffer[Int]`, required `?1.Context`
}
I would be grateful if someone could give me pointers.
Hopefully having a kind of realistic use case will make my question clearer, but I’m happy to write a smaller example if necessary.