screwlisp proposes kittens

Interactive host / visitor kitten live kittendb interaction in embeddable common lisp

A simple case of kitten server โ€ฆ kitten visitor interaction is where each kitten pageload increases the number of kittens on the page by one, and the kitten server operator can both read, and arbitrarily change this number live (for the next page load).

In practice, different tls authenticated kitten visitors will get different abilities, but to start with, the host and the visitor can interact through kittenโ€™s database via kittenโ€™s dynamic page generation as you are reading here.

For this example, I am running Kittenโ€™s kitten serve and kitten shell from inside of embeddable common lisp, which can be freely interleaved into C++ programs. They are being started as external processes, and communicated with textually. kitten shell uses an (offline javascript) nodejs base. I refered to turning-C+ยฑprograms-into-kittens as my insane kitten fairy godmother theory.

Lisp format and read-char-no-hang

I previously gave an example of reading streams to their end without blocking via lispโ€™s read-char-no-hang.

Here also, I am sending strings to the kitten shell via common lispโ€™s format. If you are not using embeddable common lisp, you can send the kitten/shell commands directly to your shell somehow else.

(format stream "~a~%" "foo") sends foo\ to stream stream.

Kitten

In #P"~/kittens/dynamicdb/" I have the following

#P"index.page.js"

if (kitten.db.numbers === undefined) kitten.db.numbers = { count: 2 };

export default () => kitten.html`<h1>Kitten count</h1>
<p>${'๐Ÿฑ'.repeat(kitten.db.numbers.count++) }
`

being somewhere between the kitten dynamic page tutorial and Kitten persistence tutorial.

Let us use this from lisp at all.

Embeddable common lisp ext:run-program "kitten"

Setup

#|
 (eepitch-shell)
cd ~/kittens/dynamicdb
ecl
|#

If you are not using eepitch I guess you can figure out what you would do.

Run Kitten inside of ecl - kitten serve

Noting we cd(1)'d into our directory already, letโ€™s use embeddable common lispโ€™s ext:run-program to just start serving the kitten.

(locally
    (declare (special *kit2way* *kitret* *kitproc*))
  (multiple-value-setq
      (*kit2way* *kitret* *kitproc*)
    (ext:run-program "kitten" '("serve")
		     :wait nil)))

(loop :for ch := (read-char-no-hang
		  (two-way-stream-input-stream *kit2way*))
      :while ch :do (princ ch))

kitten serve output

ECL (Embeddable Common-Lisp) 21.2.1 (git:UNKNOWN)
Copyright (C) 1984 Taiichi Yuasa and Masami Hagiya
Copyright (C) 1993 Giuseppe Attardi
Copyright (C) 2013 Juan J. Garcia-Ripoll
Copyright (C) 2018 Daniel Kochmanski
Copyright (C) 2021 Daniel Kochmanski and Marius Gerbershagen
ECL is free software, and you are welcome to redistribute it
under certain conditions; see file 'Copyright' for details.
Type :h for Help.  
Top level in: #<process TOP-LEVEL 0x7ff63765df80>.
> (locally
    (declare (special *kit2way* *kitret* *kitproc*))
  (multiple-value-setq
      (*kit2way* *kitret* *kitproc*)
    (ext:run-program "kitten" '("serve")
		     :wait nil)))

#<two-way stream 0x7ff635669500>
> 
(loop :for ch := (read-char-no-hang
		  (two-way-stream-input-stream *kit2way*))
      :while ch :do (princ ch))
๐Ÿฑ Kitten
   by ]8;;https://ar.alAral Balkan]8;;, ]8;;https://small-tech.orgSmall Technology Foundation]8;;

   Version 0-046649-22.11.0-20250502014448
   Born 2025/05/02 at 01:44:48 UTC (Taurus)
   Fav. colour #046649 โ–ˆโ–ˆโ–ˆ
   API version 0
   Runtime Node.js 22.11.0
 โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ
 โ”‚ Like this? Fund us!                       โ”‚
 โ”‚                                           โ”‚
 โ”‚ Weโ€™re a tiny, independent not-for-profit. โ”‚
 โ”‚ ]8;;https://small-tech.org/fund-ushttps://small-tech.org/fund-us]8;;            โ”‚
 โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ
   Need help?
   ]8;;https://kitten.small-web.org/tutorials/Tutorials]8;; โ€ข ]8;;https://kitten.small-web.org/reference/Reference]8;; โ€ข ]8;;https://codeberg.org/kitten/app/issuesIssues]8;;
domainsIncludingRedirectedOnes [ 'localhost' ]

๐ŸŒ Domain  ]8;;https://localhosthttps://localhost 
]8;;๐Ÿ“‚ Source  ]8;;file:///home/me/kittens/dynamicdb๐Ÿ /kittens/dynamicdb]8;;
๐Ÿ’พ Data    ]8;;/home/me/.local/share/small-tech.org/kitten/data/home.me.kittens.dynamicdb.localhost.443Open app data folder]8;;

โ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œ

๐Ÿ”’ Donโ€™t forget to create your identity

   ]8;;https://localhost/๐Ÿ’•/hello/probably-dont-share/One-time use identity creation link]8;;

โ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œโ•Œ

   Server is running and listening for connectionsโ€ฆ

๐Ÿข Interactive shell is available on 127.0.0.1, port 1337.
๐Ÿข To access it, run kitten shell.
NIL
> 

Okay! It seems like our kitten is being served. However, the ansi escape codes are just printing, but the kittencode characters are great.

Start a kitten shell as described

(locally
    (declare (special *2way* *ret* *proc*))
  (multiple-value-setq
      (*2way* *ret* *proc*)
    (ext:run-program "kitten" '("shell")
		     :wait nil)))

(loop :for ch := (read-char-no-hang
		  (two-way-stream-input-stream *2way*))
      :while ch :do (princ ch))

โฌ‡

> (locally
    (declare (special *2way* *ret* *proc*))
  (multiple-value-setq
      (*2way* *ret* *proc*)
    (ext:run-program "kitten" '("shell")
		     :wait nil)))

#<two-way stream 0x7ff634f89b40>
> 
(loop :for ch := (read-char-no-hang
		  (two-way-stream-input-stream *2way*))
      :while ch :do (princ ch))
๐Ÿฑ Kitten
   by ]8;;https://ar.alAral Balkan]8;;, ]8;;https://small-tech.orgSmall Technology Foundation]8;;

   Version 0-046649-22.11.0-20250502014448
   Born 2025/05/02 at 01:44:48 UTC (Taurus)
   Fav. colour #046649 โ–ˆโ–ˆโ–ˆ
   API version 0
   Runtime Node.js 22.11.0
 โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ
 โ”‚ Like this? Fund us!                       โ”‚
 โ”‚                                           โ”‚
 โ”‚ Weโ€™re a tiny, independent not-for-profit. โ”‚
 โ”‚ ]8;;https://small-tech.org/fund-ushttps://small-tech.org/fund-us]8;;            โ”‚
 โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ
   Need help?
   ]8;;https://kitten.small-web.org/tutorials/Tutorials]8;; โ€ข ]8;;https://kitten.small-web.org/reference/Reference]8;; โ€ข ]8;;https://codeberg.org/kitten/app/issuesIssues]8;;
๐Ÿฑ ๐Ÿ’ฌ (127.0.0.1:41028) 
NIL
> 

I guess if we were in a term those ANSI terminal escape codes would work.

Add a numbers (javascript) object to the kittendb

If you have visited the page, the kittenโ€™s page generation will have populated this already. If not it will not exist yet. It is not a problem to attempt to run this and fail in the kitten shell.

(format (two-way-stream-output-stream *2way*)
	"~a~%"
	"kitten.db.numbers = { count: 3 }")

(loop :for ch := (read-char-no-hang
		  (two-way-stream-input-stream *2way*))
      :while ch :do (princ ch))

โฌ‡

> (format (two-way-stream-output-stream *2way*)
	"~a~%"
	"kitten.db.numbers = { count: 3 }")

NIL
> 
(loop :for ch := (read-char-no-hang
		  (two-way-stream-input-stream *2way*))
      :while ch :do (princ ch))
kitten.db.numbers = { count: 3 }
Uncaught:

  Error: Table numbers already exists. To replace it, please first call await <database>.numbers.__table__.delete().
      at _JSDB.setHandler (file:///home/me/.local/share/small-tech.org/kitten/app/kitten-bundle.js:243715:15)
๐Ÿฑ ๐Ÿ’ฌ (127.0.0.1:41028) 
NIL
> 

Read the kittendb current count

(format (two-way-stream-output-stream *2way*)
	"~a~%"
	"kitten.db.numbers.count")

(loop :for ch := (read-char-no-hang
		  (two-way-stream-input-stream *2way*))
      :while ch :do (princ ch))

โฌ‡


(Interactively) set a new value in the kittendb object

(format (two-way-stream-output-stream *2way*)
	"~a~%"
	"kitten.db.numbers.count = 1")

(loop :for ch := (read-char-no-hang
		  (two-way-stream-input-stream *2way*))
      :while ch :do (princ ch))

โฌ‡

> (format (two-way-stream-output-stream *2way*)
	"~a~%"
	"kitten.db.numbers.count = 1")

NIL
> 
(loop :for ch := (read-char-no-hang
		  (two-way-stream-input-stream *2way*))
      :while ch :do (princ ch))
kitten.db.numbers.count = 1

  1

๐Ÿฑ ๐Ÿ’ฌ (127.0.0.1:41028) 
NIL

Get kittendb value

(format (two-way-stream-output-stream *2way*)
	"~a~%"
	"kitten.db.numbers.count")

(loop :for ch := (read-char-no-hang
		  (two-way-stream-input-stream *2way*))
      :while ch :do (princ ch))

โฌ‡

> (format (two-way-stream-output-stream *2way*)
	"~a~%"
	"kitten.db.numbers.count")

NIL
> 
(loop :for ch := (read-char-no-hang
		  (two-way-stream-input-stream *2way*))
      :while ch :do (princ ch))
kitten.db.numbers.count

  1

๐Ÿฑ ๐Ÿ’ฌ (127.0.0.1:41028) 
NIL

Killing the kitten

Unfortunate heading aside.

(format (two-way-stream-output-stream *2way*)
	"~a~%"
	"process.kill(0)")

This seems to end up suddenly killing the whole process for me, after which I might as well

#|
 (eepitch-kill)
|#

for good measure.

Conclusions

Simple host-visitor communication is seen via the visitor hitting a kitten and the host reading and changing the number of kittens on the kittenloads interactively, live in response.

In practice, the host will be a robot system connecting two visitorsโ€™ interactions (visitor + host๐Ÿค– + visitor), but this article showed one-half of the dynamic in interactive practice (host + visitor)

My model for kittens in lisp is to make an elephant out of kittens using the mop.

Fin.

Kitten steps. What do you think (on the Mastodon thread please) about Kitten, lisp, the example interaction, the direction.

screwlisp proposes kittens