Proposal to add keyword arguments in the next release


#1

I’m thinking about adding support for keyword arguments in the next release of uLisp. So, for example, instead of having to remember that the command to define an I/O pin as INPUT_PULLUP is:

(pinmode pin 2)

you will be able to write:

(pinmode pin :input-pullup)

This will be especially useful with the Arduino function analogreference which will be supported in the next version of uLisp, as some platforms support multiple options. For, example, on the Arduino Mega 2560 you will be able to specify any of the options:

(analogreference :default)
(analogreference :internal1v1)
(analogreference :internal2v56)
(analogreference :external)

The keyword parameters will automatically pick up the values they represent from the appropriate Arduino core, so for example it’s not a problem if the number representing :input-pullup is different on two different platforms.

The simplest way to implement the keyword parameters will be to include them in the table of built-in symbols, called lookup_table[] in the uLisp source. However, there’s a problem: because the keywords are platform-specific, and there’s a different number of keywords on each platform, they will have to go at the end of the table with #defines to select the keywords for each platform. This will slightly complicate the procedure for Adding your own functions, which currently doesn’t require you to renumber the existing function entries in the table when you add new ones.

Does this sound like a useful feature? Can you think of any other uses for these keyword parameters? Any suggestions about the implementation?


#2

I think you might wish to consider adding them only to the ARM variant, if they take up RAM, but not to AVR.

(Moreover, I think a builtin “equal” might be useful — eq is a bit annoying when working with list comparisons, and pretty much one of the very first things I always do is to define “equal”, define a “member”-function matching on “equal”, and so forth — and exactly “equal” is a well-known keyword argument.)


#3

I’ve thought of a way of doing the keyword arguments that won’t take up RAM, and I’ve even got it working nicely on the Arduino Uno, so I hope that puts your mind at rest!

I’ll think about equal


#4

I’d prefer to have named constants (not only keywords) separate from functions, in separate C table without need to keep enums in sync.


#5

I don’t quite understand what you mean. Can you give an example?


#6

To have separate table with constants like
{“INPUT-PULLUP-PIN”, “2”}, {“OUTPUT-PIN”, “4”}, etc.
that will be compiled into ROM/PROGMEM and the value in second column will be read every time the symbol in first column is referenced. So

(pinmode pin INPUT-PULLUP-PIN)

is evaluated as

(pinmode pin (read-from-string "2"))


#7

The reason I chose to use keywords for these Arduino options is that the values of the options are arbitrary, processor-specific numbers, and there isn’t any benefit in the user being able to access them. Keywords evaluate to themselves; for example:

> :input-pullup
:input-pullup

Keywords are used for this sort of function in Common Lisp; for example, the function open lets you specify the direction as :input or :output.

If I represented these options as constants in a separate table, as you’re suggesting, there would be two possible approaches. Either they could be defined in RAM, like other user-defined symbols, in which case they would use up RAM (and space in the symbol table). Alternatively they could be in a separate constants table in PROGMEM, in which case I’d have to scan that table separately when a form is entered in the REPL, which would be an overhead. My proposal of including them in the main lookup_table[] avoids this overhead.

What application have you got in mind that would benefit from your approach?


#8

I have no problem with keywords but one needs to define the corresponding values somewhere anyway. Precompiled constants are conceptually simpler to me. But both can be implemented no problem, say, when in the proposed table below the value is NULL, name is treated as a keyword.

Example what I have in mind with separate table: I have following color definitions in LispLibrary, and it now takes nontrivial amount of RAM even for unused colors:

(defvar TFT-BLACK #x0000)
(defvar TFT-NAVY #x000F)
(defvar TFT-DARKGREEN #x03E0)
(defvar TFT-DARKCYAN #x03EF)
(defvar TFT-MAROON #x7800)
(defvar TFT-PURPLE #x780F)
(defvar TFT-OLIVE #x7BE0)
(defvar TFT-LIGHTGREY #xD69A)
(defvar TFT-DARKGREY #x7BEF)
(defvar TFT-BLUE #x001F)
(defvar TFT-GREEN #x07E0)
(defvar TFT-CYAN #x07FF)
(defvar TFT-RED #xF800)
(defvar TFT-MAGENTA #xF81F)
(defvar TFT-YELLOW #xFFE0)
(defvar TFT-WHITE #xFFFF)
(defvar TFT-ORANGE #xFDA0)
(defvar TFT-GREENYELLOW #xB7E0)
(defvar TFT-PINK #xFE19)
(defvar TFT-BROWN #x9A60)
(defvar TFT-GOLD #xFEA0)
(defvar TFT-SILVER #xC618)
(defvar TFT-SKYBLUE #x867D)
(defvar TFT-VIOLET #x915C)

With separate PROGMEM-only constant table, value will be read into RAM only if constant is referenced

typedef struct {
  PGM_P name;
  PGM_P value;
} constant_entry_t;

const constant_entry_t lookup_table[]{
//color definitions from TFT_eSPI.h, #stringify the values
{PGM("TFT-BLACK"), PGM(#TFT_BLACK)},...

#9

The Adafruit libraries I’m referencing for graphics include these predefined colours:

// Some ready-made 16-bit ('565') color settings:
#define ST77XX_BLACK 0x0000
#define ST77XX_WHITE 0xFFFF
#define ST77XX_RED 0xF800
#define ST77XX_GREEN 0x07E0
#define ST77XX_BLUE 0x001F
#define ST77XX_CYAN 0x07FF
#define ST77XX_MAGENTA 0xF81F
#define ST77XX_YELLOW 0xFFE0
#define ST77XX_ORANGE 0xFC00

I can include keywords for these, called something a bit friendlier, like:

:color-orange

Would that be OK?


#10

It’s fine. As long as adding new colors and other constants won’t require editing in 3 places like functions do.