@@ -74,7 +74,7 @@ when defined(nimHasEffectsOf):
74
74
else :
75
75
{.pragma: effectsOf.}
76
76
77
- import std/ typetraits
77
+ import std/ [ typetraits, macros]
78
78
79
79
when defined(nimPreviewSlimSystem):
80
80
import std/ assertions
@@ -379,3 +379,129 @@ proc unsafeGet*[T](self: Option[T]): lent T {.inline.}=
379
379
## Generally, using the `get proc <#get,Option[T]>`_ is preferred.
380
380
assert self.isSome
381
381
result = self.val
382
+
383
+ macro evalOnceAs(expAlias, exp: untyped ): untyped =
384
+ ## Injects `expAlias` in caller scope, evaluating it only once and ensuring no copies are made.
385
+ expectKind(expAlias, nnkIdent)
386
+ return if exp.kind != nnkSym:
387
+ newLetStmt(expAlias, exp)
388
+ else:
389
+ newProc(name = genSym(nskTemplate, $expAlias), params = [getType(untyped ) ],
390
+ body = exp, procType = nnkTemplateDef)
391
+
392
+ template withValue* [T](source: Option[T]; varname, ifExists, ifAbsent: untyped ) =
393
+ ## Reads a value from an Option, assigns it to a variable, and calls `ifExists` when it is `some`.
394
+ ## If the value is `none`, it calls `ifAbsent`.
395
+ runnableExamples:
396
+ some("abc").withValue(foo):
397
+ assert foo == "abc"
398
+ do:
399
+ assert false
400
+
401
+ var absentCalled: bool
402
+ none(int ).withValue(foo):
403
+ assert false
404
+ do:
405
+ absentCalled = true
406
+ assert absentCalled
407
+
408
+ block:
409
+ evalOnceAs(local, source)
410
+ if local.isSome:
411
+ template varname(): auto {.inject, used.} = unsafeGet(local)
412
+ ifExists
413
+ else:
414
+ ifAbsent
415
+
416
+ template withValue*[T](source: Option[T]; varname, ifExists: untyped ) =
417
+ ## Reads a value from an Option, assigns it to a variable, and calls `ifExists` when it is `some`.
418
+ runnableExamples:
419
+ some("abc").withValue(foo):
420
+ assert foo == "abc"
421
+
422
+ none(int ).withValue(foo):
423
+ assert false
424
+
425
+ source.withValue(varname, ifExists):
426
+ discard
427
+
428
+ template mapIt*[T](value: Option[T], action: untyped ): untyped =
429
+ ## Applies an action to the value of the `Option`, if it has one.
430
+ runnableExamples:
431
+ assert some(42).mapIt(it * 2).mapIt($it) == some("84")
432
+ assert none(int ).mapIt(it * 2).mapIt($it) == none(string )
433
+
434
+ block:
435
+ type InnerType = typeof(
436
+ block:
437
+ var it {.inject, used.}: typeof(value.get())
438
+ action
439
+ )
440
+
441
+ var outcome: Option[InnerType]
442
+ value.withValue(it):
443
+ outcome = some(action)
444
+ outcome
445
+
446
+ template flatMapIt*[T](value: Option[T], action: untyped ): untyped =
447
+ ## Executes an action on the value of the `Option`, where that action can also return an `Option`.
448
+ runnableExamples:
449
+ assert some(42).flatMapIt(some($it)) == some("42")
450
+ assert some(42).flatMapIt(none(string )) == none(string )
451
+ assert none(int ).flatMapIt(some($it)) == none(string )
452
+ assert none(int ).flatMapIt(none(string )) == none(string )
453
+
454
+ block:
455
+ type InnerType = typeof(
456
+ block:
457
+ var it {.inject, used.}: typeof(value.get())
458
+ action.get()
459
+ )
460
+
461
+ var outcome: Option[InnerType]
462
+ value.withValue(it):
463
+ outcome = action
464
+ outcome
465
+
466
+ template filterIt*[T](value: Option[T], action: untyped ): Option[T] =
467
+ ## Tests the value of the `Option` with a predicate, returning a `none` if it fails.
468
+ runnableExamples:
469
+ assert some(42).filterIt(it > 0) == some(42)
470
+ assert none(int ).filterIt(it > 0) == none(int )
471
+ assert some(-11).filterIt(it > 0) == none(int )
472
+
473
+ block:
474
+ var outcome = value
475
+ outcome.withValue(it):
476
+ if not action:
477
+ outcome = none(T)
478
+ do:
479
+ outcome = none(T)
480
+ outcome
481
+
482
+ template applyIt*[T](value: Option[T], action: untyped ) =
483
+ ## Executes a code block if the `Option` is `some`, assigning the value to a variable named `it`
484
+ runnableExamples:
485
+ var value: string
486
+ some("foo").applyIt:
487
+ value = it
488
+ assert value == "foo"
489
+
490
+ none(string ).applyIt:
491
+ assert false
492
+
493
+ value.withValue(it):
494
+ action
495
+
496
+ template `or`*[T](a, b: Option[T]): Option[T] =
497
+ ## Returns the value of the `Option` if it has one, otherwise returns the other `Option`.
498
+ runnableExamples:
499
+ assert((some(42) or some(9999)) == some(42))
500
+ assert((none(int ) or some(9999)) == some(9999))
501
+ assert((none(int ) or none(int )) == none(int ))
502
+ block:
503
+ evalOnceAs(local, a)
504
+ if local.isSome:
505
+ local
506
+ else:
507
+ b
0 commit comments