What new features would you like to see in uLisp?


#1

What new features would you like to see in a future version of uLisp? Here are some things I have been thinking about, in no particular order. For each item I’ve rated them according to how hard I think they will be to implement:

* is easy, ** is moderate, and *** is hard.

Let me know how useful you think these would be, or suggest any additional features I haven’t included on the list:

Arrays *

Simple one-dimensional arrays, which would use make-array to create an array; for example:

(defvar data (make-array 1000 :initial-element 0))

You could then read array entries with aref, and assign values to array elements using aref with the in-place functions such as setf, incf, and decf to change array elements. This would provide a more efficient alternative to using nth with a list, and would be useful for data logging, or mathematical operations.

It may be possible to extend the implementation to include two-dimensional arrays.

Hash tables ***

Hash tables, using make-hashtable to create a hash table, gethash to read entries, and gethash with the in-place functions such as setf, incf, and decf to add or change entries. This would be useful for processing strings, and sparse arrays.

Format *

A simplified version of Common Lisp’s format command, for printing or outputting information in a formatted way.

Currently if you want to print out a series of values you have to do something like:

(princ "Time = ") (princ time) (princ ", Value = ") (princ value) (terpri)

The format function would let you write:

(format t "Time = ~a, Value = ~a ~%" time value)

It would probably be feasible to include a field-width parameter, but not to offer a range of floating-point format options.

Stack overflow ***

Better trapping of a stack overflow, with a uLisp error, rather than possibly causing uLisp to hang. I’ve already managed to implement this in the ARM version of uLisp, but it would require a bit more work to add it to the other versions; it may not even be possible.

For example, you can trigger this condition intentionally by doing:

(defun fill () (fill) nil)
(fill)

The nil stops uLisp optimising the recursive call to (fill) into a simple loop. The latest ARM version of uLisp will stop with a Stack overflow error, from which you can recover and continue using uLisp, but other versions will just hang up, requiring a reset

Backtrace **

Allow errors to give a backtrace, for better debugging, so you can see where the error occurred and how the program got there. Also, it would allow you to examine variables, etc.

unwind-protect ***

Add unwind-protect, so that program execution can continue after an error, without returning to the REPL. This was suggested by Philipp Marek, but would be difficult to implement.

Outputting to a string *

Add the ability to write output to a string, rather than to a stream, using with-output-to-string.

Interrupts **

Some time ago I tested an interrupt facility with uLisp, to allow you to detect events independently of the execution of your Lisp program. It would avoid the need to sit in a loop repeatedly testing the input from a pushbutton, for example.

It would use an attach-interrupt function, which would allow you to assign a Lisp function to be called when the interrupt occurs. For example, to count the number of interrupts on INT0 on an AVR processor:

(defvar c 0)
(defun inc (n) (incf c n))
(attach-interrupt 0 inc)

The function you supply to attach-interrupt would take one parameter, the number of interrupts that have occurred since the function was last called.

A time function *

It’s currently possible to test how long a benchmark takes to run by using the for-millis form. For example:

(for-millis () (print (tak 18 12 6)))

A time function would have two advantages; it could print execution times longer than 32767 milliseconds on 8/16-bit versions of uLisp, and it could include information about the number of garbage collections that have occurred.

Assembler **

The ARM and RISC-V versions of uLisp allow you to generate machine-code functions, and execute them like Lisp functions. An assembler written in Lisp lets you write these machine-code functions using assembler mnemonics.

It should be relatively easy to add this facility to the ESP and STM32 versions of uLisp. It may be possible to add it to the AVR and MSP430 versions of uLisp.

Feedback please

Look forward to your feedback!


#2

I have to say they’re all interesting, but arrays and hash tables seem particularly interesting from my perspective. I’d have thought arrays would require a substantial change to the way memory allocation works in order to work efficiently; is that wrong?


#3

You’re absolutely right, so to fit in with my memory model I was planning to implement arrays using a binary tree. This wouldn’t be as efficient as a conventional array, using sequential memory, but it would still be a big improvement over using a list. For example, a 1024-element array would require 10 steps to index to an element rather than, on average, 512 when using a list.


#4

Sending output to a string might be helpful for sending strings to I/O displays.
A time function would be welcomed.
Maybe a version that runs on Linux?

Honestly, the one thing that could get me to use µLisp more would be a more
interactive REPL. I like to sit at the terminal and tinker so when I make ONE
LITTLE mistake I hate having to retype everything again. TAB completion
on global function and variable names, including the builtins, would be slick too.

Just some thoughts…


#5

Maybe a version that runs on Linux?

The Arduino IDE runs on Linux, so as far as I know all the versions of uLisp should also run fine on Linux, although I admit I haven’t tested them. Perhaps someone can confirm.


#6

Honestly, the one thing that could get me to use µLisp more would be a more
interactive REPL.

I agree that this is needed, and I’d welcome suggestions.

I tend to develop Lisp programs by having a text editor window open on the same computer. I compose the functions in the text editor, and then copy and paste them into the Arduino IDE’s Serial Monitor, but it would work for the terminal too.


#7

Yes, the Arduino IDE runs just fine on Linux. I was asking for the uLisp interpreter to run on Linux, not the microcontrollers.


#8

I think the meaning is to run uLisp itself within Linux. I’ve been thinking about looking to see if I can port the ARM version to run on Raspberry Pi. It should be doable, as there are some fairly Arduino-like libraries for using the GPIO features on the Pi.

I think that functionality would blow up the ATmega328, but the main question would be which terminal to emulate or respond to. Ecma48 control codes should be most of what is needed, but I think Windows doesn’t really do those until with the new terminal program that isn’t installed by default.


#9

I think what’s needed is a more general solution - a better way of interacting with uLisp running on a microcontroller when developing applications.

Perhaps I’ll create a new thread to discuss the options.


#10

One feature I think might be worth having, especially as the various versions pick up functionality that isn’t entirely shared, is *features*.


#11

4 posts were split to a new topic: uLisp on non-microcontrollers


#12

Output to strings strikes me as interesting.

And I, too, vote for “version on Linux”! To produce there a standalone interpreter!

Also — version for iOS! There is NONE, presently. All their “Lisps” are Schemes.

Speaking of which, I also vote for “version on CP/M” and perhaps even “version for MS-DOS”! 😁 Seriously, CP/M is POOR in that regard. The best thing they have is Mulisp, and it is non-standard. More standard is XLISP, but it’s a RAM hog.

I KNOW “there are more capable variants on Linux” and I don’t care. I want to be sure, exactly what I develop will run also on the microcontroller.


#14

A post was merged into an existing topic: uLisp on non-microcontrollers


#16

Hi David,

Good to hear from you again , hope your doing well.

I would be glad with following (in order of importance to me, being the first most important and going to less).

1- Format
2- Arrays
3- Outputting to a string
4- Interrupt would be nice to have (ARM)

So I have chosen the easiest things almost :-)

Kind regards,
Ronny


#17

Oh David ,

And I totally agree with some members here , a backspace in the REPL would be very nice , in case you make a mistake in typing.

Regards ,
Ronny


#18

Regarding the “bsckspace in the REPL”-topic, David suggested in the past, when we talked about editors, a buffer. But the idea to have a compile-time option is interesting indeed. It does not need even to “go far”, “ability to correct the last ten characters” or something would already be a huge improvement.


#19

See the new thread about this:


#20

I think this is a good idea, but implementing it presents some problems.

There aren’t yet any built-in variables in uLisp, or keywords, and if *features* was a list the user could add to, like in Common Lisp, rather than a fixed list generated from uLisp, then it would need to be in workspace memory and would use up some of the limited memory available.

Any suggestions?


#21

Unless you expect to eventually add keywords to uLisp, there’s no real reason not to just use plain symbols. After all, the only real reason Common Lisp has keywords is to address the package system and the resulting uncertainty about which package a symbol is being read into. uLisp doesn’t have that.

As for implementation, it might be possible to hardcode a list in flash memory, and have the evaluator special-case the symbol *features* so it returns a reference to that list if nothing is found in the environment. That way, using the default features doesn’t cost anything in workspace, but it’s a little pricier in code size.

Thinking about it, I should probably just try and implement something like that, see if it works…


#22

I’ve been thinking that it would be very easy to add keywords to uLisp, if they would be useful for anything else.

They’re quite useful as a way of defining enumerated types, for use in case, etc.