Skip to content

Commit f8eb263

Browse files
authored
Optimise some result methods (#28)
Fix: forall on Err should be true, change exists to be its own method rather than an alias add aliases isOkAnd, isErrOr
2 parents f8c6eac + 9312501 commit f8eb263

File tree

2 files changed

+50
-30
lines changed

2 files changed

+50
-30
lines changed

src/main/scala/steps/result/package.scala

Lines changed: 43 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -127,11 +127,11 @@ enum Result[+T, +E] extends IterableOnce[T]:
127127

128128
def iterator: Iterator[T] = this match
129129
case Ok(value) => Iterator.single(value)
130-
case Err(_) => Iterator.empty
130+
case _ => Iterator.empty
131131

132132
override def knownSize: Int = this match
133133
case Ok(_) => 1
134-
case Err(_) => 0
134+
case _ => 0
135135

136136
end Result
137137

@@ -177,17 +177,29 @@ object Result:
177177
*/
178178
def isErr: Boolean = r.isInstanceOf[Err[?]]
179179

180-
/** Returns `true` if result contains an [[Ok]] value that satisfies `pred`.
180+
/** Returns `true` if result is an [[Err]], or if result contains an [[Ok]] value that satisfies `pred`.
181181
* @group access
182182
*/
183183
def forall(pred: T => Boolean): Boolean = r match
184184
case Ok(value) => pred(value)
185-
case _ => false
185+
case _ => true // if no elem then true for all elem
186186

187-
/** Returns `true` if result contains an [[Ok]] value that satisfies `pred`.
187+
/** Returns `true` if result is exactly an [[Ok]] containing a value that satisfies `pred`.
188188
* @group access
189189
*/
190-
def exists(pred: T => Boolean): Boolean = forall(pred)
190+
def exists(pred: T => Boolean): Boolean = r match
191+
case Ok(value) => pred(value)
192+
case _ => false // does not exist if no elem
193+
194+
/** Alias of [[exists]].
195+
* @group access
196+
*/
197+
inline def isOkAnd(pred: T => Boolean): Boolean = exists(pred)
198+
199+
/** Alias of [[forall]].
200+
* @group access
201+
*/
202+
inline def isErrOr(pred: T => Boolean): Boolean = forall(pred)
191203

192204
// Conversion to Option and other Seqs
193205

@@ -197,7 +209,7 @@ object Result:
197209
*/
198210
def toOption: Option[T] = r match
199211
case Ok(value) => Some(value)
200-
case Err(_) => None
212+
case _ => None
201213

202214
/** Returns the [[Err]] error from the result.
203215
* @group convert
@@ -211,8 +223,8 @@ object Result:
211223
* @group convert
212224
*/
213225
def toSeq: Seq[T] = r match
214-
case Ok(value) => Seq(value)
215-
case _ => Seq()
226+
case Ok(value) => Seq(value) // backend currently optimises this to ::(value, Nil)
227+
case _ => Seq() // backend currently optimises this to Nil
216228

217229
// Conversion to Either
218230

@@ -258,26 +270,28 @@ object Result:
258270
*/
259271
def getOrElse(default: => T): T = r match
260272
case Ok(value) => value
261-
case Err(_) => default
273+
case _ => default
262274

263275
// Tapping
264276

265277
/** Runs `peek` with the wrapped [[Ok]] value, if it exists.
266278
* @group access
267279
*/
268280
def tap(peek: T => Unit): r.type =
281+
// TODO: align with STDLIB we'd use [U] which avoids "discarded non-unit value" warning
269282
r match
270283
case Ok(value) => peek(value)
271-
case Err(_) => ()
284+
case _ => ()
272285
r
273286

274287
/** Runs `peek` with the wrapped [[Err]] error, if it exists.
275288
* @group access
276289
*/
277290
def tapErr(peek: E => Unit): r.type =
291+
// TODO: align with STDLIB we'd use [U] which avoids "discarded non-unit value" warning
278292
r match
279-
case Ok(_) => ()
280293
case Err(error) => peek(error)
294+
case _ => ()
281295
r
282296

283297
// Combinators
@@ -288,11 +302,11 @@ object Result:
288302
* @group combine
289303
*/
290304
def and[U, E1](other: => Result[U, E1]): Result[(T, U), E | E1] = r match
291-
case err @ Err(_) => err
305+
case err: Err[E] => err
292306
case Ok(t) =>
293307
other match
294-
case Ok(u) => Ok((t, u))
295-
case err @ Err(_) => err
308+
case Ok(u) => Ok((t, u))
309+
case err: Err[E1] => err
296310

297311
/** Returns a tuple of `r` and `other` if both are [[Ok]], or the first
298312
* error otherwise. Short-circuits, so `other` is not evaluated if `r` is
@@ -330,36 +344,36 @@ object Result:
330344
infix def cons[Ts <: Tuple](
331345
other: Result[Ts, List[E]]
332346
): Result[T *: Ts, List[E]] = (r, other) match
333-
case (Ok(t), Ok(ts)) => Ok(t *: ts)
334-
case (Err(e), Ok(_)) => Err(List(e))
335-
case (Ok(_), err @ Err(es)) => err
336-
case (Err(e), Err(es)) => Err(e :: es)
347+
case (Ok(t), Ok(ts)) => Ok(t *: ts)
348+
case (Err(e), Ok(_)) => Err(List(e))
349+
case (Ok(_), err @ Err(_)) => err
350+
case (Err(e), Err(es)) => Err(e :: es)
337351

338352
/** Generalized version of [[zip]] to work with arbitrary tuples.
339353
* Operator version of [[cons]]
340354
* @see
341355
* [[zip]], [[cons]]
342356
* @group combine
343357
*/
344-
infix def `*:`[Ts <: Tuple](
358+
infix inline def `*:`[Ts <: Tuple](
345359
other: Result[Ts, List[E]]
346360
): Result[T *: Ts, List[E]] = r.cons(other)
347361

348362
/** Returns `r` if it is [[Ok]], otherwise evaluates and returns `other`.
349363
* @group combine
350364
*/
351365
def or[E1](other: => Result[T, E1]): Result[T, E1] = r match
352-
case ok: Ok[T] => ok
353-
case Err(_) => other
366+
case ok: Ok[T] => ok
367+
case _ => other
354368

355369
// transformers
356370

357371
/** Maps the [[Ok]] value through `f`, otherwise keeping the [[Err]] error.
358372
* @group transform
359373
*/
360374
def map[U](f: T => U): Result[U, E] = r match
361-
case Ok(value) => Ok(f(value))
362-
case err @ Err(_) => err
375+
case Ok(value) => Ok(f(value))
376+
case err: Err[E] => err
363377

364378
/** Maps the [[Err]] error through `f`, otherwise keeping the [[Ok]] value.
365379
* @group transform
@@ -373,8 +387,8 @@ object Result:
373387
* @group transform
374388
*/
375389
def flatMap[U](f: T => Result[U, E]): Result[U, E] = r match
376-
case Ok(value) => f(value)
377-
case err @ Err(_) => err
390+
case Ok(value) => f(value)
391+
case err: Err[E] => err
378392

379393
/** Returns the output of `f` from applying it to the [[Err]] case error,
380394
* otherwise keeping the [[Ok]] case. Similar to [[flatMap]], but on the
@@ -400,8 +414,8 @@ object Result:
400414
* @group access
401415
*/
402416
def getNullable: T | Null = r match
403-
case Ok(value) => value
404-
case err: Err[E] => null
417+
case Ok(value) => value
418+
case _ => null
405419

406420
// Conversion to `Try`
407421

@@ -423,7 +437,7 @@ object Result:
423437
body: => T
424438
): Result[T, E] =
425439
try Ok(body)
426-
catch case ex => Err(catcher.applyOrElse(ex, _ => throw ex))
440+
catch case ex => Err(catcher.applyOrElse(ex, throw _))
427441

428442
/** Right unit for chains of [[cons]]. An [[Ok]] with an [[EmptyTuple]] value.
429443
* @group construct

src/test/scala/steps/result/Result.scala

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,16 @@ class ResultTest extends munit.FunSuite {
1919

2020
assert(ok.forall(_ == 1))
2121
assert(!ok.forall(_ == 2))
22+
assert(ok.isErrOr(_ == 1))
23+
assert(!ok.isErrOr(_ == 2))
2224
assert(ok.exists(_ == 1))
2325
assert(!ok.exists(_ == 2))
24-
assert(!err.forall(_ == 1))
26+
assert(ok.isOkAnd(_ == 1))
27+
assert(!ok.isOkAnd(_ == 2))
28+
assert(err.forall(_ == 1) /* forall is always true for Err */)
29+
assert(err.isErrOr(_ == 1) /* isErrOr is always true for Err */)
2530
assert(!err.exists(_ == 1))
31+
assert(!err.isOkAnd(_ == 1))
2632

2733
assertEquals(ok.get, 1)
2834
assert(Try(err.get).isFailure)

0 commit comments

Comments
 (0)