I have a situation where a codepath is given some Path
's which it uses to query a database. It so happens that sometimes some of these paths need to go through a translation service, to map them to updated Path
s before the query. Essentially the simplified flow is
val paths: List[Path] = getPaths()
val pathTranslationOpt: Option[Map[Path, Path]] = getTranslationOpt()
paths.foreach { p =>
val pathToUse =
pathTranslationOpt
.flatMap(_.get(p))
.getOrElse(p)
someFunctionsUsingThePath(pathToUse)
}
I'm worried about a situation in which someone accidentally calls someFunctionsUsingThePath
with p
, forgetting to perform the translation. This could be caught at compile time modifying the code slightly
val paths: List[Path] = getPaths()
// let NewPath be a case class defined in the scope of the translate function as
// case class NewPath(path: Path)
val pathTranslationOpt: Option[Map[Path, NewPath]] = getTranslationOpt()
paths.foreach { p =>
// Have the function explicitely take a NewPath so the user cannot forget to call the translation service
someFunctionsUsingThePath(
newPathOpt =
pathTranslationOpt.flatMap(_.get(p)),
fallbackPath = p
)
}
This seems to be safer to me, as forgetting to call the translation service results in a compile time error. However, a negligent user could simply pass in Some(NewPath(p))
to the function call, defeating the purpose.
Is there a way to make NewPath
such that it can be used freely, but only constructed from the translation call?
Try this:
case class NewPath private(path: Path)
object NewPath {
def apply(path: Path): NewPath =
NewPath(
pathTranslationOpt.flatMap(_.get(path)).getOrElse(path)
)
}
def someFunctionsUsingThePath(path: NewPath) = ???
paths.foreach { p =>
someFunctionsUsingThePath(NewPath(p))
}
NewPath
can only be created via the apply
method in the companion object, which translates the path if necessary but otherwise leaves it unchanged.