@@ -74,7 +74,7 @@ when defined(nimHasEffectsOf):
7474else :
7575 {.pragma : effectsOf.}
7676
77- import std/ typetraits
77+ import std/ [ typetraits, macros]
7878
7979when defined (nimPreviewSlimSystem):
8080 import std/ assertions
@@ -379,3 +379,129 @@ proc unsafeGet*[T](self: Option[T]): lent T {.inline.}=
379379 # # Generally, using the `get proc <#get,Option[T]>`_ is preferred.
380380 assert self.isSome
381381 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