-
Notifications
You must be signed in to change notification settings - Fork 383
/
Union.scala
115 lines (95 loc) · 4.17 KB
/
Union.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
/* __ *\
** ________ ___ / / ___ __ ____ Scala.js API **
** / __/ __// _ | / / / _ | __ / // __/ (c) 2013-2015, LAMP/EPFL **
** __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \ http://scala-lang.org/ **
** /____/\___/_/ |_/____/_/ | |__/ /____/ **
** |/____/ **
\* */
package scala.scalajs.js
import scala.language.implicitConversions
import scala.language.higherKinds
import scala.scalajs.js
/** Value of type A or B (union type).
*
* Scala does not have union types, but they are important to many
* interoperability scenarios. This type provides a (partial) encoding of
* union types using implicit evidences.
*/
sealed trait |[A, B] // scalastyle:ignore
object | { // scalastyle:ignore
/** Evidence that `A <: B`, taking `|`-types into account. */
sealed trait Evidence[-A, +B]
/** A unique (and typically dead-code-eliminated away) instance of
* `Evidence`.
*/
private object ReusableEvidence extends Evidence[scala.Any, scala.Any]
abstract sealed class EvidenceLowestPrioImplicits { this: Evidence.type =>
/** If `A <: B2`, then `A <: B1 | B2`. */
implicit def right[A, B1, B2](implicit ev: Evidence[A, B2]): Evidence[A, B1 | B2] =
ReusableEvidence.asInstanceOf[Evidence[A, B1 | B2]]
/** Given a covariant type constructor `F[+_]`, if `A <: B`, then
* `F[A] <: F[B]`.
*/
implicit def covariant[F[+_], A, B](implicit ev: Evidence[A, B]): Evidence[F[A], F[B]] =
ReusableEvidence.asInstanceOf[Evidence[F[A], F[B]]]
/** Given a contravariant type constructor `F[-_]`, if `B <: A`, then
* `F[A] <: F[B]`.
*/
implicit def contravariant[F[-_], A, B](implicit ev: Evidence[B, A]): Evidence[F[A], F[B]] =
ReusableEvidence.asInstanceOf[Evidence[F[A], F[B]]]
}
abstract sealed class EvidenceLowPrioImplicits
extends EvidenceLowestPrioImplicits {
this: Evidence.type =>
/** `Int <: Double`, because that's true in Scala.js. */
implicit def intDouble: Evidence[Int, Double] =
ReusableEvidence.asInstanceOf[Evidence[Int, Double]]
/** If `A <: B1`, then `A <: B1 | B2`. */
implicit def left[A, B1, B2](implicit ev: Evidence[A, B1]): Evidence[A, B1 | B2] =
ReusableEvidence.asInstanceOf[Evidence[A, B1 | B2]]
}
object Evidence extends EvidenceLowPrioImplicits {
/** `A <: A`. */
implicit def base[A]: Evidence[A, A] =
ReusableEvidence.asInstanceOf[Evidence[A, A]]
/** If `A1 <: B` and `A2 <: B`, then `A1 | A2 <: B`. */
implicit def allSubtypes[A1, A2, B](
implicit ev1: Evidence[A1, B], ev2: Evidence[A2, B]): Evidence[A1 | A2, B] =
ReusableEvidence.asInstanceOf[Evidence[A1 | A2, B]]
}
/** Upcast `A` to `B1 | B2`.
*
* This needs evidence that `A <: B1 | B2`.
*/
implicit def from[A, B1, B2](a: A)(implicit ev: Evidence[A, B1 | B2]): B1 | B2 =
a.asInstanceOf[B1 | B2]
/** Upcast `F[A]` to `F[B]`.
*
* This needs evidence that `F[A] <: F[B]`.
*/
implicit def fromTypeConstructor[F[_], A, B](a: F[A])(
implicit ev: Evidence[F[A], F[B]]): F[B] =
a.asInstanceOf[F[B]]
/** Operations on union types. */
implicit class UnionOps[A <: _ | _](val self: A) extends AnyVal {
/** Explicitly merge a union type to a supertype (which might not be a
* union type itself).
*
* This needs evidence that `A <: B`.
*/
def merge[B](implicit ev: |.Evidence[A, B]): B =
self.asInstanceOf[B]
}
/** Provides an [[Option]]-like API to [[js.UndefOr]]. */
implicit def undefOr2ops[A](value: js.UndefOr[A]): js.UndefOrOps[A] =
new js.UndefOrOps(value)
/* This one is not very "in the spirit" of the union type, but it used to be
* available for `js.UndefOr[A]`, so we keep it for backward source
* compatibility. It is not really harmful, and has some perks in certain
* interoperability scenarios.
*/
implicit def undefOr2jsAny[A](value: js.UndefOr[A])(
implicit ev: A => js.Any): js.Any = {
value.map(ev).asInstanceOf[js.Any]
}
}