#* and #** in Hy

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(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 ".")

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)

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)

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)

=> (print "friendly" #* greeting #** config :flush True)

=> (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.


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s