The
phantom type idiom can help to solve the difficulty of distinguishes Monitors that wrap the same type. Type erasure unfortunately limits its usefulness.
A bettter approach is to create a case class for each type, using inheritance to provide the boilerplate.
abstract class BaseMonitor[T, S](implicit p: S => T) {
def s: S
private val v = p(s)
def apply[R](f: T => R) = synchronized { f(v) }
}
Example
implicit def toDom(s: String): Document = (DocumentBuilderFactory.newInstance().newDocumentBuilder()).parse(new InputSource(new StringReader(s)))
case class XA(s: String) extends BaseMonitor[org.w3c.dom.Document, String]
val mdoc = XA("value")
mdoc { _.getDocumentElement.getTextContent } must beEqualTo("value")
Note that the only field in the case class must be named
s to match the abstract method in BaseMonitor.
The common BaseMonitor type structure can be extracted as follows
type XML = BaseMonitor[org.w3c.dom.Document, String]
case class XA(s: String) extends XML
case class XB(s: String) extends XML