An implementation of partial function application for Nim.
It is inspired by the way Scala does it (with _ as arguments), but it takes into account Nim's current limitations on type inference.
partial macros
partial macros transform the passed called proc expression into a partially applied one as an anonymous proc.
For example: partial(1 * ?:float) will generate proc (a1: float): auto = 1 * a1
Usage examples:
import pkg/funcynim/[chain, partialproc], std/[sugar] proc plus[T](a, b: T): T = a + b proc `not`[T](predicate: T -> bool): T -> bool = predicate >> partial(not ?_) proc `?`(c: char): string = $c.int let f1 = partial(0 + ?:int) f2 = partial(5.plus(?:int)) f3 = partial(plus(left ?: uint, 6)) f4 = partial(?:string & ?:char) f5 = not partial(?:int < 0) f6 = partial(?:string & (?'c')) f7 = partial(`*`(n ?: int, n))) doAssert(f1(1) == 1) doAssert(f2(10) == 15) doAssert(f3(154u) == 160u) doAssert(f4("abc", 'd') == "abcd") doAssert(f5(1)) doAssert(f6("abc") == "abc" & ?'c') doAssert(f7(8) == 8 * 8)
Placeholders
A placeholder can be specified in place of an argument in the passed call expression. Each of them will generate a parameter for the returned anonymous proc.
The types of placeholders are.
- The identifier one (?someIdent)
- The type one (?:SomeType)
- The fully specified one (a ?: Slice[int])
If a placeholder expression is nested inside an another one, such as parentheses, the whole expression will not be parsed. Instead, it will be considered as a regular argument and copied as is.
Identifier
Examples:
- ?a
- ?a1_b
- ?someFloat
- ? someParam (space after ? is allowed)
- ?_ (if the compiler can infer the type)
Rules:
- The matching generated parameter in the returned lambda will have the name passed after ?, unless the passed identifier is _, where the name will be auto-generated.
- The passed identifier must be a direct valid Nim identifier, i.e. not constructed.
- The passed identifier can be referred to in the partial expression, like a regular parameter. For example, partial(?x == x)(1) is equivalent to (x => x == x)(1).
- Its type will be auto. Depending on where the partial macro is used, a type placeholder may be needed to help type inference.
Type
Examples:
- ?:string
- ?: ref Exception (space after ?: is allowed)
- ?:(a.typeof())
- ?:auto (equivalent to ?_)
Rules:
- The matching generated parameter in the returned lambda will be of the type passed after ?:.
- The expression after ?: can be anything as long as it is semantically a typedesc.
- The name of the generated parameter will be auto-generated.
Fully specified
Examples:
- n ?: Natural
- rngSeed?:int (Spaces around ?: are optional, but should be put for readability.)
- _ ?: float (equivalent to ?:float)
- kind ?: auto (equivalent to ?kind)
- _ ?: auto (equivalent to ?_)
Rules:
- A mix of the previous ones except the user can decide for both the name and the type of the generated parameter at the same time.
- Because ?: is an infix operator, it may not play well with other infix operators. For example, partial(a ?: int + b) does not compile, but partial(`+`(a ?: int, b)) does work.
Macros
macro partial(call: untyped{call | (nkCall | nkCommand | nkPrefix | nkInfix)}): proc
-
Accepts a call expression like one of the following:
- Regular call: f(a, ?b, ...)
- Command call: f ?:char (more than 1 argument will make the compiler complain)
- Method style call: a.f(?b, ...)
- Prefix operator: - ?:Natural, $ ?s
- Infix operator: ?a + b, a .. ?:bool
See the module documentation for details on how to use the macro.
Note: The "call" constraint was here first and has been kept for API compatibility, in case some usages have been missed or overlooked. For example, it allows the passed expression to be a postfix operator or a string literal call, but these were not planned to work with the macro. It will be removed or replaced in the next major release.Source Edit macro partial(expr: untyped{nkBracketExpr}): proc
-
Accepts a subscript expression like:
- a[?b]
- a[?:int, ...]
See the module documentation for details on how to use the macro.
Since 1.1.0.
Source Edit