I'm using two libraries which define string interpolators (simplified code for clarity):
implicit class LiteralsOps(val sc: StringContext) extends AnyVal {
def uri(args: Any*): Uri = macro LiteralSyntaxMacros.uriInterpolator
(...)
def ipv4(args: Any*): Uri.Ipv4Address = macro LiteralSyntaxMacros.ipv4AddressInterpolator
def ipv6(args: Any*): Uri.Ipv6Address = macro LiteralSyntaxMacros.ipv6AddressInterpolator
}
ip4s:
implicit class IpLiteralSyntax(val sc: StringContext) extends AnyVal {
def ip(args: Any*): IpAddress = macro LiteralSyntaxMacros.ipInterpolator
def ipv4(args: Any*): Ipv4Address = macro LiteralSyntaxMacros.ipv4Interpolator
def ipv6(args: Any*): Ipv6Address = macro LiteralSyntaxMacros.ipv6Interpolator
(...)
def host(args: Any*): Hostname = macro LiteralSyntaxMacros.hostnameInterpolator
}
I want to use them both, namely the uri
interpolator from Http4s and the various interpolators from ip4s. The problem is that the following code:
import com.comcast.ip4s._
import org.http4s.syntax.literals._
class Foo(cidr: Cidr[Ipv4Address] = ipv4"192.168.1.1" / 24)
Fails to compile with:
[error] Note that implicit conversions are not applicable because they are ambiguous:
[error] both method IpLiteralSyntax in package ip4s of type (sc: StringContext): com.comcast.ip4s.package.IpLiteralSyntax
[error] and method http4sLiteralsSyntax in trait LiteralsSyntax of type (sc: StringContext): org.http4s.syntax.LiteralsOps
[error] are possible conversion functions from StringContext to ?{def ipv4: ?}
[error] class Foo(cidr: Cidr[Ipv4Address] = ipv4"192.168.1.1" / 24)
Is there anyway to shadow/remove a string interpolator from the implicit scope?
Your problem is obviously that there are conflicting interpolators for ipv4
and ipv6
and compiler doesn't know which one to use.
Conflicting implicits problem can be solved by giving one of implicits higher priority. This can be done by using putting a lower-priority implicits into a trait and then extend an object, that declares higher-priority implicits.
Implicits from http4s
can be brought into scope with trait AllSyntax
:
import com.comcast.ip4s._
import org.http4s.Uri
import org.http4s.syntax.AllSyntax
case class Foo(cidr: Cidr[Ipv4Address] = ipv4"192.168.1.1" / 24)
class MyApp extends AllSyntax {
val testUri: Uri = uri"http://test.pl" //we can still use uri interpolator from http4s
}
But this still wouldn't compile:
import org.http4s.Uri
import org.http4s.syntax.AllSyntax
import com.comcast.ip4s._
class MyApp extends AllSyntax {
val testUri: Uri = uri"http://test.pl"
val ip = ipv4"192.168.1.1" / 24 //compile error
}
Unfortunately, ip4s
doesn't provide any trait to bring implicits into scope, so we could prioritize them.
What you could do is creating another object, where you'd copy internals of package object com.comcast.ip4s
and then extend AllSyntax:
import org.http4s.Uri
import org.http4s.syntax.AllSyntax
import com.comcast.ip4s._
import scala.language.experimental.macros
object MySyntax extends AllSyntax {
//copied from com.comcast.ip4s
final implicit class IpLiteralSyntax(val sc: StringContext) extends AnyVal {
def ip(args: Any*): IpAddress = macro LiteralSyntaxMacros.ipInterpolator
def ipv4(args: Any*): Ipv4Address =
macro LiteralSyntaxMacros.ipv4Interpolator
def ipv6(args: Any*): Ipv6Address =
macro LiteralSyntaxMacros.ipv6Interpolator
(...)
}
}
Then you could use it like this:
class MyApp extends App {
import MySyntax._
val testUri: Uri = uri"http://test.pl"
val ip = ipv4"192.168.1.1" / 24 //interpolator from com.comcast.ip4s has higher priority
}