-
Notifications
You must be signed in to change notification settings - Fork 705
/
LazyOption.scala
204 lines (156 loc) · 6.64 KB
/
LazyOption.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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
package scalaz
/** [[scala.Option]], but with a value by name. */
sealed abstract class LazyOption[A] extends Product with Serializable {
import LazyOption._
import LazyEither._
// import newtypes.{FirstLazyOption, LastLazyOption}
def fold[X](some: (=> A) => X, none: => X): X =
this match {
case LazySome(z) => some(z())
case LazyNone() => none
}
def ?[X](some: => X, none: => X): X =
fold(_ => some, none)
def isDefined: Boolean =
fold(_ => true, false)
def isEmpty: Boolean =
!isDefined
def getOrElse[AA >: A](default: => AA): AA =
fold(a => a, default)
def |[AA >: A](default: => AA): AA =
getOrElse(default)
def exists(f: (=> A) => Boolean): Boolean =
fold(f, false)
def forall(f: (=> A) => Boolean): Boolean =
fold(f, true)
def toOption: Option[A] =
fold(a => Some(a), None)
def toLazyRight[X](left: => X): LazyEither[X, A] =
fold(lazyRight(_), lazyLeft(left))
def toLazyLeft[X](right: => X): LazyEither[A, X] =
fold(lazyLeft(_), lazyRight(right))
def toRight[X](left: => X): (X \/ A) =
fold(\/-(_), -\/(left))
def toLeft[X](right: => X): (A \/ X) =
fold(-\/(_), \/-(right))
def toList: List[A] =
fold(_ :: Nil, Nil)
def orElse(a: => LazyOption[A]): LazyOption[A] =
fold(_ => this, a)
/* TODO
def first: FirstLazyOption[A] =
this.*-->[FirstLazyOption[A]]
def last: LastLazyOption[A] =
this.*-->[LastLazyOption[A]]
*/
def map[B](f: (=> A) => B): LazyOption[B] =
fold(a => lazySome(f(a)), lazyNone)
def foreach(f: (=> A) => Unit): Unit =
fold(f, ())
def filter(f: (=> A) => Boolean): LazyOption[A] =
fold(a => if (f(a)) this else lazyNone, lazyNone)
def flatMap[B](f: (=> A) => LazyOption[B]): LazyOption[B] =
fold(f, lazyNone)
def ap[B](f: => LazyOption[A => B]): LazyOption[B] =
fold(a => f map (_.apply(a)), lazyNone)
def traverse[G[_] : Applicative, B](f: (=> A) => G[B]): G[LazyOption[B]] =
fold(
some = x => Applicative[G].map(f(x))(b => lazySome(b)),
none = Applicative[G].point(lazyNone[B])
)
def foldRight[B](z: => B)(f: (A, => B) => B): B =
fold(
some = a => f(a, z),
none = z
)
def zip[B](b: => LazyOption[B]): LazyOption[(A, B)] =
for {
x <- this
y <- b
} yield (x, y)
def unzip[X, Y](implicit ev: A <:< (X, Y)): (LazyOption[X], LazyOption[Y]) =
fold(xy => (lazySome(xy._1), lazySome(xy._2)), (lazyNone, lazyNone))
}
private final case class LazySome[A](a: () => A) extends LazyOption[A]
private final case class LazyNone[A] private() extends LazyOption[A]
private object LazyNone {
private[scalaz] val none = LazyNone[Any]()
}
sealed abstract class LazyOptionInstances {
import LazyOption._
implicit val lazyOptionIsCovariant: IsCovariant[LazyOption] =
IsCovariant.force[LazyOption]
implicit val lazyOptionInstance: Traverse[LazyOption] & MonadPlus[LazyOption] & Alt[LazyOption] & BindRec[LazyOption] & Cozip[LazyOption] & Zip[LazyOption] & Unzip[LazyOption] & Align[LazyOption] & Cobind[LazyOption] & Optional[LazyOption] & IsEmpty[LazyOption] =
new Traverse[LazyOption] with MonadPlus[LazyOption] with Alt[LazyOption] with BindRec[LazyOption] with Cozip[LazyOption] with Zip[LazyOption] with Unzip[LazyOption] with Align[LazyOption] with Cobind[LazyOption] with Optional[LazyOption] with IsEmpty[LazyOption] {
def cobind[A, B](fa: LazyOption[A])(f: LazyOption[A] => B): LazyOption[B] = map(cojoin(fa))(f)
override def cojoin[A](a: LazyOption[A]) = a match {
case LazyNone() => lazyNone
case o @ LazySome(_) => LazySome(() => o)
}
def traverseImpl[G[_]: Applicative, A, B](fa: LazyOption[A])(f: A => G[B]): G[LazyOption[B]] = fa traverse (a => f(a))
override def foldRight[A, B](fa: LazyOption[A], z: => B)(f: (A, => B) => B): B = fa.foldRight(z)(f)
override def ap[A, B](fa: => LazyOption[A])(f: => LazyOption[A => B]): LazyOption[B] = fa ap f
def plus[A](a: LazyOption[A], b: => LazyOption[A]): LazyOption[A] = a orElse b
def alt[A](a: => LazyOption[A], b: => LazyOption[A]): LazyOption[A] = plus(a, b)
def bind[A, B](fa: LazyOption[A])(f: A => LazyOption[B]): LazyOption[B] = fa flatMap (a => f(a))
def point[A](a: => A): LazyOption[A] = lazySome(a)
def empty[A]: LazyOption[A] = lazyNone
def cozip[A, B](a: LazyOption[A \/ B]) =
a.fold({
case -\/(a) => -\/(lazySome(a))
case \/-(b) => \/-(lazySome(b))
}, -\/(lazyNone))
def zip[A, B](a: => LazyOption[A], b: => LazyOption[B]) = a zip b
def unzip[A, B](a: LazyOption[(A, B)]) = a.unzip
def alignWith[A, B, C](f: A \&/ B => C) = (a, b) =>
a.fold(
aa => lazySome(f(b.fold(
bb => \&/.Both(aa, bb), \&/.This(aa)))), b.fold(
bb => lazySome(f(\&/.That(bb))), lazyNone))
def pextract[B, A](fa: LazyOption[A]): LazyOption[B] \/ A =
fa.fold(a => \/-(a), -\/(lazyNone))
override def isDefined[A](fa: LazyOption[A]): Boolean = fa.isDefined
@scala.annotation.tailrec
def tailrecM[A, B](a: A)(f: A => LazyOption[A \/ B]): LazyOption[B] =
f(a) match {
case LazyNone() => lazyNone
case LazySome(t) => t() match {
case \/-(b) => lazySome(b)
case -\/(a0) => tailrecM(a0)(f)
}
}
}
implicit def lazyOptionEqual[A: Equal]: Equal[LazyOption[A]] = {
import std.option._
Equal.equalBy(_.toOption)
}
implicit def lazyOptionMonoid[A: Semigroup]: Monoid[LazyOption[A]] =
new Monoid[LazyOption[A]] {
def zero = lazyNone
def append(a: LazyOption[A], b: => LazyOption[A]) = (a, b) match {
case (LazySome(a1), LazySome(b1)) => LazySome(() => Semigroup[A].append(a1(), b1()))
case (LazySome(_) , LazyNone()) => a
case (LazyNone() , b1 @ LazySome(_)) => b1
case (LazyNone() , LazyNone()) => lazyNone
}
}
implicit def lazyOptionShow[A](implicit S: Show[A]): Show[LazyOption[A]] =
Show.shows(_.fold(a => "LazySome(%s)".format(S.shows(a)), "LazyNone"))
/* TODO
implicit def LazyOptionOrder[A: Order]: Order[LazyOption[A]] =
Order.orderBy(_.toOption)*/
}
object LazyOption extends LazyOptionInstances {
def lazySome[A](a: => A): LazyOption[A] =
LazySome(() => a)
def lazyNone[A]: LazyOption[A] =
LazyNone.none.asInstanceOf[LazyOption[A]]
def fromOption[A](oa: Option[A]): LazyOption[A] = oa match {
case Some(x) => lazySome(x)
case None => lazyNone[A]
}
/**
* Returns the given argument in `lazySome` if this is `true`, `lazyNone` otherwise.
*/
def condLazyOption[A](value: Boolean, a: => A): LazyOption[A] = if (value) lazySome(a) else lazyNone
}