src/applicates

  Source   Edit

Macros

macro applicate(body): untyped
syntax for applicates with no parameters, or for applicates made with do notation

Example:

const defineX = applicate:
  let x {.inject.} = 3
defineX.apply()
doAssert x == 3

# this is how do notation works, which we unfortunately can't show
# as an example because runnableexamples breaks do notation:

# const incr = applicate do (x: int) -> int: x + 1
# doAssert incr.apply(x) == 4

# named:

# applicate named do (a, b; c: int):
#   let a = b(c)
# named.apply(upperA, char, 65)
# doAssert upperA == 'A'
  Source   Edit
macro applicate(params, body): untyped

generates a template based on the params and body and registers it as a typed applicate. when anonymous, returns applicate id literal (compatible with ApplicateArg/Applicate). when a name is specified (using call/object constructor syntax), defines that name as a constant with the value being the applicate id.

param syntax: p: T, or p is T if Nim doesn't like colons, and paramList -> T for return type or paramsList: T for return type if paramsList is in parentheses. type annotations that are grouped together like a, b: int resolve to a: int, b: int.

the param syntax might be removed in the future given that do directly replicates regular routine parameters

note: the return type is untyped by default unlike templates, where it is void

Example:

applicate foo(name: untyped, val: int):
  let name = val

foo.apply(x, 5)
doAssert x == 5

template double(appl: ApplicateArg) =
  appl.apply()
  appl.apply()

var c = 0
when false: # runnableExamples does not handle do: correctly, this works otherwise
  double(applicate do:
    double(applicate do:
      double(applicate do: inc c)))
else:
  double applicate(
    double applicate(
      double applicate(inc(c))))
doAssert c == 8
  Source   Edit
macro apply(appl: ApplicateArg; args: varargs[untyped]): untyped
applies the applicate by injecting the applicate routine (if not in scope already) then calling it with the given arguments

Example:

const incr = toApplicate(system.succ)
doAssert incr.apply(1) == 2
  Source   Edit
macro forceApply(appl: ApplicateArg; args: varargs[untyped]): untyped

applies the applicate by injecting the applicate routine, even if already in scope, then calling it with the given arguments

realistically, the applicate routine is never in scope, but if you really come across a case where it is then you can use this

  Source   Edit
macro forceToSymbol(appl: ApplicateArg): untyped
retrieves the symbol of the applicate, also instantiates routine definitions, without reusing definitions in scope   Source   Edit
macro instantiateAs(appl: ApplicateArg; name: untyped): untyped

instantiates the applicate in the scope with the given name

helps where apply syntax isn't enough (for example generics and overloading)

Example:

instantiateAs(toApplicate(system.succ), incr)
doAssert incr(1) == 2
proc foo[T](x, y: T): T {.makeApplicate.} = x + y
proc bar(x, y: string): string {.makeApplicate.} = x & y
instantiateAs(foo, baz)
instantiateAs(bar, baz)
doAssert baz(1.0, 2.0) == 3.0
doAssert baz[uint8](1, 2) == 3u8
doAssert baz("a", "b") == "ab"

# also works but less efficient as new template is generated:
instantiateAs(toApplicate(`-`), minus)
doAssert minus(4) == -4
doAssert minus(5, 2) == 3
  Source   Edit
macro makeApplicate(body): untyped
Registers given routine definitions as applicates and assigns each applicate to a constant with the name of its routine.

Example:

proc foo: auto {.makeApplicate.} = x
block:
  let x = 5
  doAssert foo.apply() == 5
block:
  let x = "abc"
  doAssert foo.apply() == "abc"
  Source   Edit
macro makeApplicateFromTyped(body: typed): untyped

Registers applicate with given routine(s), but forces it to be type checked first. This lets it use symbols that are accessible during the registering, but if the routine is not a template then only local symbols are accessible.

Works best for templates and macros, so applicate uses this.

Note: This will generate an unused warning for the given routine, makeTypedApplicate automatically generates a used pragma but only on untyped routine expressions.

  Source   Edit
macro makeTypedApplicate(body: untyped): untyped

Calls makeApplicateFromTyped without giving an unused warning.

Accomplishes this by injecting {.used.}.

Example:

makeTypedApplicate:
  template useIt =
    doAssert it == realIt

let realIt = 5
block:
  let it = realIt
  useIt.apply()
  Source   Edit
macro toApplicate(sym: untyped): Applicate
directly registers sym as an applicate node. might be more efficient than toCallerApplicate for most cases, and accepts varying arities

Example:

const plus = toApplicate(`+`)
doAssert plus.apply(1, 2) == 3
  Source   Edit
macro toCallerApplicate(sym: typed): Applicate

infers the arity of sym from its symbol then calls toCallerApplicate(sym, arity)

if sym is a symbol choice, then the common arity of the choices is used. if the symbol choices do not share an arity, it will give an error

Example:

const newstr = toCallerApplicate(newString)
var s: string
s.setLen(4)
doAssert newstr.apply(4) == s

const leq = toCallerApplicate(`<=`)
doAssert leq.apply(1, 2)
doAssert leq.apply(2.0, 2.0)
  Source   Edit
macro toCallerApplicate(sym: untyped; arity: static int): Applicate
creates an applicate of a template with n = arity untyped parameters that calls the given symbol sym

Example:

const adder = toCallerApplicate(`+`, 2)
doAssert adder.apply(1, 2) == 3
  Source   Edit
macro toSymbol(appl: ApplicateArg): untyped
retrieves the symbol of the applicate, also instantiates routine definitions

Example:

template foo(x: int): int = x + 1
const incr = toApplicate(foo)
doAssert toSymbol(incr)(1) == 2
  Source   Edit

Templates

template `&&`(sym): untyped
same as toApplicate(sym)

Example:

const foo = &&min
doAssert foo.apply(1, 2) == 1
  Source   Edit
template `==>`(body): untyped
same as applicate(body)   Source   Edit
template `==>`(params, body): untyped
infix version of applicate, same parameter syntax

Example:

doAssert (x ==> x + 1).apply(2) == 3
const foo = (a, b) ==> a + b
doAssert foo.apply(1, 2) == 3
  Source   Edit