Extracting arbitrary types

Sometimes, you’re trying to extract content from strings into something that is neither a primitive type, a tuple or a case class. Dealing with arbitrary types is essentially the same thing as with case classes, you just need to write your own instantiation function.

Let’s imagine for example that we want to extract the bits between brackets as points with an optional z-coordinate:

val input = "(1, 2) and then (3, 4) followed by (5, 6, 7)"

This could be achieved with the following regular expression:

import kantan.regex.implicits._

val regex = rx"\((\d+), (\d+)(?:, (\d+))?\)"

Here’s the class we want to extract matches into:

class Point(val x: Int, val y: Int, val z: Option[Int]) {
  override def toString = s"Point($x, $y, $z)"

Just like we did for case classes, we need to create a new MatchDecoder instance for Point. While we could write one from scratch, it’s usually easier (and easier to get right) to use one of MatchDecoder’s helper functions - in our case, decoder:

import kantan.regex._

implicit val decoder: MatchDecoder[Point] = MatchDecoder.decoder(1, 2, 3) { (x: Int, y: Int, z: Option[Int]) =>
  new Point(x, y, z)

And that’s it, we’re done: we can now evalRegex as usual, with the right type parameter:

input.evalRegex[Point](regex).foreach(println _)
// Right(Point(1, 2, None))
// Right(Point(3, 4, None))
// Right(Point(5, 6, Some(7)))

Two things worth noting about how created that decoder instance:

Other tutorials: