In a partial function implemented with pattern matching, how to make isDefined
return false
for invalid inputs that can't be included in the case pattern?
For example, I have the following decodeList
partial function:
case class Arr(items: List[Json]) extends Json
def decode(data: Json): Option[A]
def decodeList: PartialFunction[Json, List[A]] = {
case Json.Arr(items) =>
val options = items map decode
if (options forall (_.isDefined)) options map (_.get)
else throw new Error // the partial function should be undefined here
}
I want to change the code in a way so that decodeList.isDefinedAt
evaluates to false for invalid inputs. For example, for an a
that decode(a)
evaluates to None
,decodeList.isDefinedAt(Json.Arr(List(a)))
should evaluate to false
.
Or from another perspective, if I try to include the condition in the case pattern as in the following code, where shall I put the val options = items map decode
definition so it can be reusable by both the case pattern and the block?
def decodeList: PartialFunction[Json, List[A]] = {
case Json.Arr(items) if (options forall (_.isDefined)) =>
options map (_.get)
}
You can do this by defining a custom extractor object, e.g.
object Options {
def unapply(items: List[Json]) = Some(items map decode)
}
def decodeList: PartialFunction[Json, List[A]] = {
case Json.Arr(Options(options)) if (options forall (_.isDefined)) =>
options map (_.get)
}
which isn't particularly convenient, but I don't know a better way.
Of course, I'd suggest actually defining def decodeList(list: Json): Option[List[A]]
which fits better with decode
and doesn't need such workarounds; then Function.unlift(decodeList)
if you need a PartialFunction
.
def decodeList(list: Json) = list match {
case Json.Arr(items) =>
val options = items map decode
if (options forall (_.isDefined)) Some(options map (_.get)) else None
case _ => None
}