
uLisp – Lisp for the Arduino - weeber
http://www.ulisp.com/
======
sigil
This looks really neat! There was a story about Lisp at JPL that got me
dreaming about embedded REPLs:

 _> The Remote Agent software, running on a custom port of Harlequin Common
Lisp, flew aboard Deep Space 1 (DS1), the first mission of NASA's New
Millennium program. Remote Agent controlled DS1 for two days in May of 1999.
During that time we were able to debug and fix a race condition that had not
shown up during ground testing. (Debugging a program running on a $100M piece
of hardware that is 100 million miles away is an interesting experience.
Having a read-eval-print loop running on the spacecraft proved invaluable in
finding and fixing the problem.)_ [http://www.flownet.com/gat/jpl-
lisp.html](http://www.flownet.com/gat/jpl-lisp.html)

So often in embedded development I resort to things that make printf-style
debugging look downright virtuous. ("If you reach some complicated runtime
state, blink the LED thrice.") Having a REPL would let you poke around without
the compile-flash-test cycle and have an actual conversation with the
hardware.

~~~
lisper
I have TinyScheme running on a STM32F415.

[https://sc4.us/hsm/](https://sc4.us/hsm/)

~~~
sigil
I love TinyScheme. I love NaCl. This is wonderful!

~~~
lisper
Thanks. I have a few left from a limited production run that I'm selling for
$55. Send me an email if you want one. I'm also going to be doing a
kickstarter to raise money for a real production run.

------
kazinator
Though uLisp has tail-call optimization, if the C compiler doesn't, then the
garbage collector needs stack in proportion to list length:

    
    
      void markobject (object *obj) {
        if (obj == NULL) return;
      
        object* arg = car(obj);
        if (marked(obj)) return;
    
        int type = obj->type;
        mark(obj);
      
        if (type != SYMBOL && type != NUMBER) { // cons
          markobject(arg);
          markobject(cdr(obj));   // <--- TAIL CALL!
        }
      }
    

It's simple enough to obj = cdr(obj) and wrap a loop around this.

~~~
kazinator
By the way, you can't eliminate the need for a stack when traversing the cell
graph (unless you give them parent pointers, thereby hiding the stack in the
data).

That's why I wrote "in proportion to list length", not "tree structure depth"
in general.

Above, we still have recursion when marking the arg = car(obj) value. However,
Lisp lists tend to be greatly unbalanced in favor of growing in the cdr
direction; that is why it helps to iterate on the cdr.

------
AceJohnny2
The AVR cpu at the core of the Arduino uses a (modified) Harvard
architecture., which separates code from data memory. A key feature of lisp of
that code IS data. How does uLisp bridge the contradiction?

~~~
Steltek
> A key feature of lisp of that code IS data.

I think this is an oversimplification. "Code is data" means several things:

1\. Both use the same ASCII representation to humans (homoiconity)

2\. Lisp Macros operate using data traversal functions

3\. You can load and eval code on the fly

Really only #3 is invalidated under Arduino constraints. #1 is the source at
rest and #2 is compile-time.

~~~
TeMPOraL
3\. is not invalidated under Arduino in a general sense. The split into "code
memory" and "data memory" is only relevant to the meaning of "code" and "data"
assigned by uC-level assembly. There's nothing stopping your code to
reinterpret data as code. Consider: that's how interpreters and VMs are made
:).

Moreover, 1-3 together are more than sum of its parts. The idea that "in Lisp
code = data" doesn't mean that elsewhere it doesn't; code = data is a
fundamental truth about the nature of information (that many other languages
and their ecosystems did quite a lot of work to obscure). Lisp only makes
exploiting that truth much easier than other languages.

Also, a nitpick. #2 is not "compile-time". Macros can and are expanded at what
you'd call "run-time" as well. Generally, Lisp systems don't have "compile-
time" and "run-time" you know from other languages; the split is usually read-
/ load- / compile- / run-time, with some other "times" sprinkled in (e.g.
"macroexpansion-time"), and this split is not necessarily sequential - you can
read/load/compile stuff in what would usually be called "runtime", and
read/load/compile phases have full access to features of the running Lisp
image, including changes you made previously - so, for example, code you're
just compiling can, during its read-time, refer to variables you previously
created at run-time and run computations on them.

------
nice_byte
> It's also an ideal language for expressing complex ideas, such as [...]
> finding the shortest route on a map

So, I googled "Dijkstra's Algorithm in Lisp" and got this:
[http://richardsherriff.com/?p=233](http://richardsherriff.com/?p=233)

Now, I'm no lisp expert, so I can't judge whether the author of this code
actually knows what they're doing, but I know for sure that in an imperative
language the implementation of the algorithm is much more concise and
understandable.

I have concluded from my observations that claims of Lisp being "easier",
"ideal for learning fundamental programming concepts" and "more expressive"
are exaggerated.

~~~
Volt
>in an imperative language

Lisp is capable of imperative programming.

~~~
pjmlp
I would say multi-paradigm instead.

------
pcl
_uLisp includes a mark and sweep garbage collector. Garbage collection takes
under 1 msec on an Arduino Uno or under 3 msec on an Arduino Mega 2560._

I wonder how predictable and controllable that is. Adding GC to what is likely
to be a real-time device seems like a potential showstopper. But if the
numbers can be a bit more firm, or if the application can receive alerts on
pause / resume, that might not be such a big deal.

~~~
Varriount
As I said in another thread recently, garbage collectors don't have to be of
the stop-the-world, non-deterministic kind. It's perfectly possible to have
real-time garbage collection [1], and the language I commonly work with
supports soft real-time use with the default GC [2]. I'm no expert on garbage
collection techniques, but what I have picked up is that most of the challenge
lies in timing of the collections (when to run the GC) and how to deal with
things like cycles.

[1] [http://michaelrbernste.in/2013/06/03/real-time-garbage-
colle...](http://michaelrbernste.in/2013/06/03/real-time-garbage-collection-
is-real.html)

[2] [http://nim-lang.org/docs/gc.html](http://nim-lang.org/docs/gc.html)

~~~
pcl
Yes, but the language chosen implied to me that it's using a stop-the-world
approach.

I've confirmed this by looking at the (surprisingly readable and small) code:
[http://www.ulisp.com/list?1BWZ](http://www.ulisp.com/list?1BWZ)

Also, it looks like the algorithm is mostly deterministic, so that's good from
a real-time standpoint. And it only runs if memory is about to be exhausted,
so if the application ensured that it stayed within the Arduino's memory
budget, it's essentially a no-op.

------
WillPostForFood
Lisp for Arduino sounds great. The C programming barrier has kept me from
tinkering with Arduino. Does this give you access to arduino shields/expansion
boards (wifi/gps/screens), or will that be dependent on device drivers?

~~~
askvictor
Check out esp8266/nodemcu. They can be programmed in lua or micropython (or
arduino ide). Not as much of an ecosystem/community, but growing. Includes
wifi on-chip, and dirt cheap.

~~~
TeMPOraL
There's also a Lisp for ESP8266 under development :). See:

[https://github.com/yesco/esp-lisp](https://github.com/yesco/esp-lisp)

------
fu86
Nice! This reminds me of my tiny Lisp Machine project: [https://aaron-
fischer.net/tdn](https://aaron-fischer.net/tdn)
[https://translate.google.com/translate?hl=en&sl=de&tl=en&u=h...](https://translate.google.com/translate?hl=en&sl=de&tl=en&u=https%3A%2F%2Faaron-
fischer.net%2Ftdn)

------
nikolay
This reminds me of muLISP [0].

[0]:
[https://de.wikipedia.org/wiki/MuLISP](https://de.wikipedia.org/wiki/MuLISP)

~~~
lispm
muLISP is much larger and needs a much more capable system. 16bit muLISP
officially requires 512 kbyte RAM.

ulisp runs with 2 kbyte RAM.

------
DigitalJack
Clojure is my favorite language, and I'd probably like scheme just as well if
it had the easy to use (yet advanced) Data structures of clojure. I just can't
seem to get my brain to mesh with Common Lisp yet, but I periodically dip my
toes to test the water.

That said, I just don't get statements like:

"It's also an ideal language for expressing complex ideas, such as teaching a
robot to solve mazes or finding the shortest route on a map." More ideal than
C, sure. But I don't see the huge advantage over other high level languages.

