klmr / klmr/sys

Support variable argument arity and custom actions

Open
#1 0 comments 0 reactions 1 assignee Claimed by @klmr View on GitHub
enhancement
Dominant language
R
Stars
13
Forks
1
PR merge metrics
No merged PRs in 30d

Description

Implement Python argparse’s
- [ ] `nargs` and
- [ ] `action`.
## `arity`

`nargs` should probably be [`arity`](https://en.wikipedia.org/wiki/Arity).

Conceivable arities are
- 1 (the default),
- 2, 3, …,
- sets {0, 1} (= `'?'` in Python), {0, 1, 2}, [1, 5] …. (an optional value currently has arity {0, 1}),
- [0, ∞[ (zero or more arguments, `'*'` in Python),
- [1, ∞[ (one or more arguments; identical to previous without default value, `'+'` in Python).

It is unclear how useful the set arities (except for {0, 1}) are, and Python argparse doesn’t support them. A first version should leave them out. In addition {0, 1} is captured by providing a default value. This leaves us with two cases:
- 1, 2, 3, …
- [1, ∞[

There are several ways of encoding the second case in R.
- `NA`; There’s precedence for this (`write.table` accepts a logical, a vector, or `NA` for its `col.names` argument).
- `'*'` as in Python.
- `1:n`, `n()` etc. — dplyr uses the pseudo-function `n()` in a similar case (this comes with the usual caveats for NSE, but it’s possibly my preferred option).
### Variable-length arities

Variable-length arity requires a substantial rewrite of the parser: option values must be consumed _until the next option_, but the next value starting with `-` or `--` may not actually be an option of more values need to be consumed. Consider:
- `arity(--foo)` = [1, ∞[

```
--foo 1 2 --bar 3
```

Results in `list(foo = c(1, 2), bar = 3)`
- `arity(--foo)` = 4

```
--foo 1 2 --bar 3
```

Results in `list(foo = c(1, 2, '--bar', 3))`.
### Switches

It is not clear what arity means for logical switches, since the current action for switches is not to a straightforward assignment but rather to assign the negation of the provided default value. Maybe logical default values and arity ≠ 1 should be mutually exclusive.
## `action`

Conceivable actions are “assign”, “append” (for arity ≠ 1), “count” and custom actions. Currently only “assign” is supported.
### Implementation

There are two fundamental ways of implementing this:
- Collect all option/argument values; at the end, transform them using `action`
- As soon as the value is stored, perform `action`

If no value is specified by the user, the default is assigned instead. In all cases, the current `transform` argument becomes redundant; it should probably be made obsolete.
#### Collect all values

This implies that actions are functions accepting a vector, and “assign” = `tail(., 1)`, “append” = `identity` (or `c`) and “count” = `length`. These are easily specified as arguments, except for “assign”, which, as the default action, doesn’t need to be specified. User actions such as “sum all arguments” are straightforward (e.g. just `sum`, but this requires that the type has been parsed beforehand, something that is currently only supported when a default is specified).
#### Perform action immediately

Alternatively, the current code that stores a value, `variable = value`, needs to be replaced with

``` r
variable = if (is.null(variable)) action(value) else action(variable, value)
```

In other words, `action` must accept both one and two arguments.

“assign” would be `function (a, b) if missing(b) a else b`; “append” would be `c`, and “count” would be `function (a, b) if (missing(b)) 1 else a + 1`. User actions would mostly be straightforward, for instance `+` could be specified to add all arguments.

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.