Skip to content

Instantly share code, notes, and snippets.

@p-pavel
Last active February 16, 2020 06:36
Show Gist options
  • Save p-pavel/2708ee0e7f5beac0a71551bfd885ef67 to your computer and use it in GitHub Desktop.
Save p-pavel/2708ee0e7f5beac0a71551bfd885ef67 to your computer and use it in GitHub Desktop.
/**
* Не уверен, что это то, что нужно, но это самое общее решение
* Никак не опираемся на стандартную библиотеку
* У него, возможно или наверняка, будут проблемы с боксингом, но идея такая. Дальше можно
* запинать для производительности
*
* @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