Upcoming version of Hy will have a new syntax for handling positional and keyword arguments (similar to * and ** in Python). Lets have a look what one can do with them.
When calling function #* and #** will work just like Python’s * and **. Instead of trying to come up with a convoluted example, I’m going to play around with our old trusty print function.
Interface has one &rest parameter and 4 keyword parameters as shown by REPL:
=> (help print) Help on built-in function print in module builtins: print(...) print(value, ..., sep=' ', end='\n', file=sys.stdout, flush=False) Prints the values to a stream, or to sys.stdout by default. Optional keyword arguments: file: a file-like object (stream); defaults to the current sys.stdout. sep: string inserted between values, default a space. end: string appended after the last value, default a newline. flush: whether to forcibly flush the stream.
What ever we give to print, it will try to turn into strings and print out:
=> (print "hello" "world") hello world
We can supply keyword arguments to change default behaviour of print (order of keywords isn’t significant):
=> (print "hello" "world" :end "!\n" :sep ".") hello.world!
If one (or more) of the keyword arguments were to be stored in a dictionary (one can envision some sort of central configuration for the software that dictates how printing is handled), we can use the new #** syntax:
=> (setv config {'end "!\n" 'sep "."}) => (print "hello" "world" #**config) hello.world!
Likewise, positional arguments could be supplied to us as a list that was a result of some function call (maybe somebody decided to make a flexible system, where type of greeting depends on the time of day):
=> (setv greeting ["hello" "world"]) => (print #* greeting) hello world
And these two can be combined:
=> (print #* greeting #** config) hello.world!
It’s possible to mix and match #*, #**, keywords and regular positional arguments as long as you observe some simple rules:
- positional arguments have to come first
- no argument can be specified more than once
=> (print "friendly" #* greeting #** config) friendly.hello.world! => (print "friendly" #* greeting #** config :flush True) friendly.hello.world! => (with [f (open "temp-file" "w")] ... (print #* greeting #** config :file f))
In essence #* and #** is close to apply, with the main difference that apply is a function and thus can be used in map and other constructs that use higher order functions.