Optional Keyword Arguments in J

J is great! It is a wonderful little language for data analysis tasks. However, to programmers used to working in modern dynamic languages, it sometimes feels a little restrictive. In particular, a ubiquitous feature in the post-Python (let’s call it) era is hash-maps. Even in older languages, like Common Lisp, the association list – allowing abritrary mappings between keys and values, is a very common idiom.

J exposes no native functionality that exactly meets this use case, one common application of which is named, optional arguments to functions.

In developing an interactive gnuplot interface for J, I wanted to pass optional keyword arguments to functions so that plots can be easily customized. So I developed a simple representation of key/val pairs which is efficient enough for small collections of named values.

Consider:

nil =: (0$0)

rankOne =: 1: -: (#@:$)
rankTwo =: 2: -: (#@:$)
talliesEven =: 0: -: (2: | #)
twoColumns =: 2: -: 1&{@:$

opts =: monad define
  keysandvals =. y
  assert. rankOne keysandvals
  assert. talliesEven keysandvals
  ii =. 2 | (i. #y)
  keys =. (-. ii) # y
  vals =. ii # y
  keys (>@:[ ; ])"(0 0) vals
)

Opts is a monadic verb which takes a flat boxed array of even length and returns a rank two boxed array whose first column is keys and whose second column is values:

opts 'key1';10;'key2';11
+----+--+
|key1|10|
+----+--+
|key2|11|
+----+--+

Look up is simple: find the key’s index in the first column and index the second column with it. Return nil if the key isn’t found.

getopt =: dyad define
  options =. x
  key =. y
  assert. rankTwo options
  assert. twoColumns options
  if. 0 -: #options do.
   nil
  else. 
    ii =. ((i.&1)@:(((key&-:@:>)"0)@:(((0&{)@:|:)))) options
    if. ii < #options do.
      (>@:(ii&{)@:(1&{)@:|:) options
    else.
      nil
    end.
  end.
)

Eg:

(opts 'key1';10;'key2';11) getopt 'key1'
-> 10

We can now define a handy conjunction to allow the specification of a default value:

dft =: conjunction define 
:
  r =. x u y
  if. r -: nil do.
   n
  else.
   r
  end.
)

Which we use this way:

(opts 'key1';10;'key2';11) getopt dft '___' 'key1'
-> 10
(opts 'key1';10;'key2';11) getopt dft '___' 'key3'
'---'

This allows us to pass optional arguments to verbs and specify default values relatively easily, as in this example from my gnuplot library:

histogram =: verb define 
  (ensureGnuPlot'') histogram y
:
  data =. y
  if. boxedP x do.
   options =. x
   gph =. options getopt dft (ensureGnuPlot'') 'gnuplot'
  else.
   gph =. x
   options =. (opt '')
  end.
  mn =. <./ data
  mx =. >./ data 
  bw =. options getopt dft (0.1&* mx-mn) 'binwidth'
  pttl =. options getopt dft '' 'plot-title'
  'binwidth=%f\n' gph gpfmt <bw
  gph send 'set boxwidth binwidth'
  gph send 'bin(x,width)=width*floor(x/width) + binwidth/2.0' 
  s =. 'plot "%s" using (bin($1,binwidth)):(1.0) smooth freq title "%s" with boxes'
  s gph gpfmt ((asFile (asVector data));pttl)
)

Where, in the dyadic case, we detect whether x is boxed, and if so, treat it as a list of options. We extract each option by name, providing a reasonable default.

This seems like a common problem, so I am wondering if anyone else in the J community has solved it before in a different way?

One thought on “Optional Keyword Arguments in J

  1. Having developed in APL in the 1980’s, before J, and also before the hashmap was even a concept, we had ‘packed objects’ which some APL’s had implemented , and for others, were implemented much like the blogger did here. This is such an old concept that time was available to implement an ‘is’ function, (which I privately call the Clinton function,) so that is’aPackedObject.aComponent’ is meaningful.

    It’s nice to see ‘mainstream’ languages catching up.

Leave a Reply

Your email address will not be published. Required fields are marked *