Last active
February 16, 2020 06:36
-
-
Save p-pavel/2708ee0e7f5beac0a71551bfd885ef67 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Не уверен, что это то, что нужно, но это самое общее решение | |
* Никак не опираемся на стандартную библиотеку | |
* У него, возможно или наверняка, будут проблемы с боксингом, но идея такая. Дальше можно | |
* запинать для производительности | |
* | |
* @see https://gist.github.com/p-pavel/2708ee0e7f5beac0a71551bfd885ef67 | |
*/ | |
object CustomCoercion { | |
/** | |
* Отношение Coercion (для типов A и B существует тип Res, к которому их можно приводить. | |
* Оно должно быть рефлексивно, транзитивно и симметрично (т.е. это отношение эквивалентности) | |
*/ | |
trait Coercion[-A,-B] { | |
type Res | |
def fromA(a: A): Res | |
def fromB(b: B): Res | |
} | |
/** Низкоприоритетные имплиситы, обеспечивающие симметричность */ | |
trait LowPri { | |
type Aux[-A, -B, R] = Coercion[A,B] {type Res = R} | |
implicit def toSecond[A, B](implicit opposite: Coercion[B,A]): Aux[A,B,opposite.Res] = new Coercion[A,B] { | |
type Res = opposite.Res | |
override def fromA(a: A):Res = opposite.fromB(a) | |
override def fromB(b: B): Res = opposite.fromA(b) | |
} | |
} | |
object Coercion extends LowPri { | |
def apply[A,B](implicit ev: Coercion[A,B]): Aux[A,B,ev.Res] = ev | |
implicit def toFirst[B,A >: B]: Aux[A,B,A] = new Coercion[A,B] {type Res = A | |
override def fromA(a: A): A = a | |
override def fromB(b: B): A = b | |
} | |
} | |
implicit class CoercionOps[A](a: A) { | |
def coerce[B](b: B)(implicit ev: Coercion[A,B]):(ev.Res, ev.Res) = (ev.fromA(a), ev.fromB(b)) | |
} | |
object TypelevelTests { | |
trait Base | |
trait Derived extends Base | |
trait Lowest extends Derived | |
val reflexivity = Coercion[Base, Base] | |
val symmetricity1: Coercion.Aux[Base, Derived, Base] = Coercion[Base, Derived] | |
val symmetricity2: Coercion.Aux[Derived, Base, Base] = Coercion[Derived, Base] | |
val transitivity1: Coercion.Aux[Lowest, Base, Base] = Coercion[Lowest, Base] | |
val transitivity2: Coercion.Aux[Base, Lowest, Base] = Coercion[Base, Lowest] | |
} | |
object ValueHolderTests { | |
trait SafeNumericCoercions { | |
implicit def int2long: Coercion.Aux[Int, Long, Long] = new Coercion[Int, Long] { | |
type Res = Long | |
override def fromA(a: Int): Long = a | |
override def fromB(b: Long): Long = b | |
} | |
} | |
trait UnsafeNumericCoercion extends SafeNumericCoercions { | |
implicit def long2double: Coercion.Aux[Long, Double, Double] = new Coercion[Long, Double] { | |
type Res = Double | |
override def fromA(a: Long): Double = a | |
override def fromB(b: Double): Double = b | |
} | |
} | |
object allNumericCoersions extends UnsafeNumericCoercion | |
final case class ValueHolder[+T](t: T) | |
object ValueHolder { | |
implicit def coercionCongruence[A, B] | |
(implicit ev: Coercion[A, B]): Coercion.Aux[ValueHolder[A], ValueHolder[B], ValueHolder[ev.Res]] = | |
new Coercion[ValueHolder[A], ValueHolder[B]] { | |
type Res = ValueHolder[ev.Res] | |
override def fromA(a: ValueHolder[A]): ValueHolder[ev.Res] = ValueHolder(ev.fromA(a.t)) | |
override def fromB(b: ValueHolder[B]): ValueHolder[ev.Res] = ValueHolder(ev.fromB(b.t)) | |
} | |
} | |
val v1 = ValueHolder(3l) | |
val v2 = ValueHolder(3.5) | |
import allNumericCoersions._ | |
import ValueHolder._ | |
val v = v1 coerce v2 | |
/* А вот это не работает. Покопаю ещё */ | |
// val transitive = ValueHolder(3) coerce ValueHolder(2.1) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment