smart arguments
- 3 minutes read - 513 wordsIt’s easy to love the terseness one can get out of a command-line tool with a “pass by order” convention. For example, maybe I run something like
deploy myapp sea2 production
to deploy an application called myapp to the second production datacenter near Seattle.
This works great, except now I need to remember the order of arguments is: deploy [app] [datacenter] [environment].
The typical solve for this problem is introducing named arguments, so you’d end up with
deploy --app myapp --datacenter sea2 --environment production
This no longer depends on the order, because we’re explicit about which value we’re passing for each argument. The command itself is a LOT longer though!
In [41]: len("deploy myapp sea2 production")
Out[41]: 28
In [42]: len("deploy --app myapp --datacenter sea2 --environment production")
Out[42]: 61
We can improve this a bit with short named arguments, like
deploy -a myapp -d sea2 -e production
In [43]: len("deploy -a myapp -d sea2 -e production")
Out[43]: 37
but now we still need to remember that the arguments are a, d, and e and which values they’ll correspond to.
Indeed, we might even have some code that checks that the value of environment is either "staging" or "production"!
So what if we turned that on its head? We can use our error checking code to automagically figure out which argument value corresponds to which argument!
formalism
- you know you need N args,
a0…aN.- In the above example,
a0=myapp,a1=sea2,a2=production.
- In the above example,
- each arg value
a_khas a qualifier predicateq_kthat returns true for a valuevifvis valid for arga_k - last
vwins (e.g.http://a.com+http://b.comprefers the second)
example 1
Suppose we want a markdown link generator, which takes two args:
- a url (like
https://traviscj.com/blog)- qualifier predicate: “starts with http”
- qualifier predicate: “contains ://”
- a slug (like
myblog)- qualifier predicate:
!url
- qualifier predicate:
example 2
Another nice example is if our arguments are a combination of app, datacenter, and environment, with values like these:
- app:
a/b/c/ … - datacenter:
sea1/sea2/sea3/ … /ord1/ord2/ord3/ … - environment:
staging/production
We can define simple predicates for the latter two:
is_datacenter(a):=a.startswith("sea") or a.startswith("ord")is_environment(a):=a in ["staging", "production"]
The app arg is a bit trickier, though, because the engineers might name things however they want!
The ideal case would be some internal app that maintains a list of valid applications, but failing that, we could also declare something like
# is_app(a) := not is_datacenter(a) and not is_environment(a)
def is_app(a):
return not is_datacenter(a) and not is_environment(a)
example 3
md link generator w/ alt text
like ex1, but:
-
a link text
- qual predicate: slug arg exists & string length greater than slug arg length
- qual predicate: any whitespace
-
test impl in
Scratch_kFFXI4
ambiguity resolutions
One trouble we might have is a given argument value qualifying for multiple arguments. When that happens, we can (roughly in order of increasing danger):
- blow up
- ask for resolution (ie prompt)
- assume a default value
- guess
- loop over possibilities
other extensions
- disqualifier predicates