I thought that if the following compiles:
implicitly[X => Y]
than so will this:
(??? :X) :Y
It turns out I was wrong. Backstory: I toyed with an implementation of type unions:
private[this] val cast = identity[Any] _
abstract class TypeUnionLevel4Implicits {
implicit def implicitUnionUnification[L, R, U <: Any | Any](implicit left :L => U, right :R => U) :(L | R) => U =
left.asInstanceOf[(L | R) => U]
}
sealed abstract class TypeUnionLevel3Implicits extends TypeUnionLevel4Implicits {
implicit def implicitRightComposedUnionMember[X, L, R <: Any | Any](implicit right :X => R) :X => (L | R) =
right.asInstanceOf[X => (L | R)]
}
sealed abstract class TypeUnionLevel2Implicits extends TypeUnionLevel3Implicits {
implicit def implicitLeftComposedUnionMember[X, L <: Any | Any, R](implicit left :X => L) :X => (L | R) =
left.asInstanceOf[X => (L | R)]
}
sealed abstract class TypeUnionLevel1Implicits extends TypeUnionLevel2Implicits {
implicit def implicitRightUnionMember[L, R] :R => (L | R) = cast.asInstanceOf[R => (L | R)]
}
abstract class TypeUnionImplicits private[slang] extends TypeUnionLevel1Implicits {
implicit def implicitLeftUnionMember[L, R] :L => (L | R) = cast.asInstanceOf[L => (L | R)]
}
object union extends TypeUnionImplicits {
type |[+L, +R]
}
Testing:
implicitly[String => (String | Int)]
implicitly[String => (Int | String)]
implicitly[(String | Int) => (Int | String)]
implicitly[String => (Int | String | Double)]
implicitly[String => (String | Int | Double | Short)]
implicitly[String => (Short | (Double | (Int | String)))]
implicitly[(String | Int | Double | Short) => (Short | Double | Int | String)]
Compiles! Success! Or is it?
val left = "left" :String | Int
val right = "right" :Int | String
val swap = (left :String | Int) :Int | String
val middle = "middle" :Int | String | Double
val farLeft = "farLeft" :String | Int | Double | Short
val farRight = "farRight" :Short | (Double | (Int | String))
val shuffle = farLeft :Short | Double | Int | String
compile...compile...compile...
Information:(14, 19) typist.this.`package`.implicitUnionUnification is not a valid implicit value for String | Int => Int | String because:
not enough arguments for method implicitUnionUnification: (implicit left: L => U, right: R => U): L | R => U.
Unspecified value parameter right.
val swap = (left :String | Int) :Int | String
Error:(14, 19) type mismatch;
found : String | Int
required: Int | String
val swap = (left :String | Int) :Int | String
I opened an issue at /dev/null because it surely must be a bug. But it looks just so plain and basic stuff, that it seems there must be a way to work around it. What I tried:
direct implicit conversions as normal methods, not function-returning ones;
an intermediate implicit class
class TypeUnionMember[X, U]
with the implicit methods above returning it instead of
X=>U
, together with one top level implicit providingX=>U
whenTypeUnionMember[X, U]
implicit exists:implicit def widenToTypeUnion[X, U](implicit unify :TypeUnionMember[X, U]) :X => U = cast.asInstanceOf[X, U]
The latter proved interesting and by interesting I mean frustrating: the logs sad that widenToTypeUnion
is an invalid implicit for String => String | Int
because an implicit TypeUnionMember[String, Nothing]
cannot be found. What?
implicitly[X => Y]
than so will this:(??? :X) :Y
Nope. #62630939 #62206440 #62751993 – TransferenceTypeUnionMember
with only a single implicit conversion declaration based on it, as per the last snippet, and it didn't change anything at all. Or did you mean for type classes to completely replace the conversion, as in no way of automatically converting from one type to another in arbitrary places? – Laveralavergne<:<
), and then define a single function that uses that evidence parameter (A <:< B
) to turn anA
into aB
, it seems to work. Here's an implementation that might suit your needs after a little polishing – WainIs
is incorrect. It should beIs[-A, +B]
. With wrong variance some things compile while they shouldn't. For exampleimplicitly[Any Is (String | Int)]
,implicitly[Any Is Nothing]
,implicitly[(Boolean | Double) Is (String | Int)]
. – Transferencetrait TC[A, B]
implicit def mkTC[A, B]: TC[A, B] = null
then an instance is foundimplicitly[TC[Int, String]]
but implicit conversionimplicit def conversion[A, B](a: A)(implicit tc: TC[A, B]): B = ???
will not workval s: String = 1 // error
. Debug logs showincompatible: (a: Int)(implicit tc: TC[Int,B]): B does not match expected type Int(1) => String
soB
is not inferred. – Transferenceimplicit def widenToTypeUnion[X, U](x: X)(implicit unify: TypeUnionMember[X, U]): U
instead. – Unvoiced