1

I am in the process of writing a small parsing routine in Scala that uses extractors to simplify parsing. During my research I discovered the conjunction pattern match (Pattern matching with conjunctions (PatternA AND PatternB)) which has been really useful. Using that I have been able to express extractors as below (outline only).

case object & {
    def unapply[T](t : T) = Some(t, t)
  }


case object ParamA {
    def unapply(jsonStr: String) : Option[String] = {
      // If param A found in json return a Some(...) else None
    ???
    }
  }
  case object ParamB {
    def unapply(jsonStr: String) : Option[String] = {
      // If param B found in json return a Some(...) else None
      ???
    }
  }
  case object ParamC {
    def unapply(jsonStr: String) : Option[String] = {
      // If param C found in json return a Some(...) else None
      ???
    }
  }

These let me match for mandatory ParamA and ParamB patterns as below.

val jsonStr = "..." // A Json string
    jsonStr match {
      case ParamA(a) & ParamB(b) => {
        // Got a and b. Now do something with it
      }
      case _ => {

      }
    }

However, if I want to match mandatory ParamA and ParamB patterns and also optionally a ParamC pattern, how would I go about expressing that in a single line?

val jsonStr = "..." // A Json string
    jsonStr match {
      case ParamA(a) & ParamB(b) & Optional(ParamC(c)) /* Is this possible? */ => {
        // Got a and b and an optional c. Now do something with it
      }
      case _ => {

      }
    }
Community
  • 1
  • 1
shizambles
  • 13
  • 4

1 Answers1

1

You could wrap value returned from unapply in another Option.

case object OptionalParamC {
    def unapply(jsonStr: String) : Option[Option[String]] = {
      // If param C found in json return a Some(Some(...)) else Some(None)
    }
  }

This way it always matches but c in OptionalParamC(c) is either Some(x) or None

More generic way would be to define extractor transformer

case object ? {

    type Extractor = {def unapply(t: String): Option[String]}

    case class Inner[T <: Extractor](extractor: T){
      def unapply(t: String) = Some(extractor.unapply(t))
    }

    def apply[T <: Extractor](extractor: T) = Inner(extractor)

  }

Then transform ParamC with it:

val OptionalC = ?(ParamC)

and finally use it like this:

ParamA(a) & ParamB(b) & OptionalC(c)

with c beeing Some(x) or None

Dominik G
  • 594
  • 6
  • 8
  • Thanks for the response. I did try that approach successfully but it has the following drawback. If in a different case clause if I look for an combination of mandatory ParamC and say optional ParamA, the extractor signatures for both will have to change to returning an Option and an Option[Option] respectively. I was hoping there was a way to express the optionality without having the extractor returning an Option nested in a Option. – shizambles Sep 04 '15 at 12:20
  • Edited response addressing issues of composability you mentioned. My guess is this is as close as it can get. – Dominik G Sep 04 '15 at 16:17