ChanServ changed the topic of #picolisp to: PicoLisp language | Channel Log: https://irclog.whitequark.org/picolisp/ | Picolisp latest found at http://www.software-lab.de/down.html | check also http://www.picolisp.com for more information
<clacke[m]> my kneejerk reaction is that NOBODY runs npm with sudo, but obviously I'm wrong
aw- has joined #picolisp
groovy2shoes has quit [Quit: moritura te salutat]
inara has quit [Quit: Leaving]
inara has joined #picolisp
rob_w has joined #picolisp
libertas has quit [*.net *.split]
libertas has joined #picolisp
PurpleMessenger[ has quit [Ping timeout: 256 seconds]
clacke[m] has quit [Ping timeout: 256 seconds]
libertas has quit [*.net *.split]
libertas has joined #picolisp
alexshendi_ has joined #picolisp
clacke[m] has joined #picolisp
<Regenaxer> ls
alexshendi_ has quit [Ping timeout: 264 seconds]
PurpleMessenger[ has joined #picolisp
freeemint has joined #picolisp
<freeemint> Hello
<Regenaxer> Hi freeemint
<freeemint> Hi
<freeemint> i founda weird quine
<freeemint> I am failing to understand 'up and was playing around: (apply '(() (up 1)) )
<Regenaxer> 'up' needs a symbol
<Regenaxer> hmm, right, special case :)
<Regenaxer> Anyway, (applt '(() (up 1))) does not make sense
<Regenaxer> apply to what?
<freeemint> quines make no sense either
<Regenaxer> Why not? No practical purpose?
<freeemint> quines take no arguements either
<freeemint> there is another thing that does not make sense to me
<freeemint> the latter half of 'server in http.l
<freeemint> (task *Sock (http @))
<freeemint> (http *Sock)
<freeemint> (or *SesId (bye))
<freeemint> (task *Sock
<freeemint> (when (accept *Sock)
<freeemint> (task @ (http @)) ) )
<freeemint> the third line is clear but the other ones do not make sense to me
<Regenaxer> The tasks are there to handle follow-up http requests over the open connection
<Regenaxer> (or *SesId (bye)) terminate if not a session (one-shot transaction)
<Regenaxer> cess11_, I do most of the stuff in files loaded at runtime, so the server is not restarted at all usually
<Regenaxer> ie instead of "!work" I use "foo/bar.l"
<Regenaxer> But right, from time to time it needs to be restarted
<freeemint> (i knew that) but i can not imagine what the code does, what does the @ in the first line stand for, why is 'http called on @ and sock, why a task in a task?
<Regenaxer> 'task' passes its key (here a socket descriptor) in '@' to the body
<freeemint> and all the code is executed in the child process?
<Regenaxer> yes
<Regenaxer> the task in the task accepts new connections
<freeemint> line one sets up a socket listener which puts everything in to http?
<Regenaxer> One connection may handle several transactions, but may be closed any time
<Regenaxer> *Sock is the main socket
<freeemint> What is a main socket?
<Regenaxer> well, "main" is not right
alexshendi_ has joined #picolisp
<Regenaxer> It is the initial socket where the session listens at
<Regenaxer> may accept new transactions any time
<Regenaxer> A session started with (app) sets up a new listening port with *Sock (port *HPorts '*Port)
<cess11_> Regenaxer: Yeah, the use cases aren't as obvious when one has your experience but for those who work through the tutorial materials for the first time this is a useful tip that could help them get jogging faster.
<Regenaxer> this new socket private to the session (child process) may accept many new connections
<Regenaxer> cess11_, true
<Regenaxer> good receipes
<tankf33der> trying implement this call in native
<freeemint> is task (task *Sock (http @)) equal to (task *Sock (http *Sock))?
<Regenaxer> freeemint, yes
<freeemint> @ is really bad at documentin stuff
<Regenaxer> tankf33der, the network functions are tough with 'native', because the structs are different on every system it seems
<tankf33der> linux is ok
<tankf33der> only linux is ok
<Regenaxer> Yes.You have to dig into the includes
<Regenaxer> Or write a C program which prints the sizes
<Regenaxer> Like src64/sysdefs.c
<tankf33der> i did it
<tankf33der> its ok too
<Regenaxer> great
<tankf33der> question about
<tankf33der> struct addrinfo **res
<tankf33der> how to pass it to native correctly
<tankf33der> now native returns -1
<Regenaxer> you need to malloc() it separately
<Regenaxer> and free() later
<tankf33der> but i dont know size of whole list in res
<Regenaxer> (native .. "malloc" gives a pointer, you pass it as 'N'
<freeemint> Why do you call (http *Sock) right after setting up the "socket hanlder" in line 1? Is it because something has already arrived at *Sock?
<Regenaxer> freeemint, yes
<Regenaxer> the initial transaction
<freeemint> oh the code than makes sense now
<tankf33der> i want translate this code
<Regenaxer> the third arg needs a pointer
<Regenaxer> what is Hints?
<Regenaxer> ah
<Regenaxer> stupid, did not see the first line ;)
<Regenaxer> Some arg must be wrong
<Regenaxer> 'res'
<Regenaxer> needs pointer to pointer, so another malloc()
<tankf33der> its close.
<Regenaxer> yes
groovy2shoes has joined #picolisp
<Regenaxer> 'doHost' in src64/net.l seems to do something similar
alexshendi_ has quit [Ping timeout: 240 seconds]
<tankf33der> (setq R (native "@" "malloc" 'N 1024))
<tankf33der> after (setq Hints (native "@" "malloc" 'N 48))
<tankf33der> didnt hel
<tankf33der> didnt help
<tankf33der> eh
<Regenaxer> (list 'R (8 . I) R)
<Regenaxer> (list 'R (8 N . 1) R) ?
<tankf33der> -1 from native for both
<Regenaxer> 8 bytes, holding a pointer, initalized with R
<Regenaxer> Needs the errno
<Regenaxer> (errno) -> cnt
<freeemint> Regenaxer can i reorder the lines the following way: https://pastebin.com/6TmXU08j
<Regenaxer> I think (task *Sock (http *Sock)) may be needed in the first transaction
<freeemint> ahh
<Regenaxer> depending if its processing also enters some wait state (eg. db sync)
<freeemint> makes sense
<Regenaxer> Perhaps seldom
<Regenaxer> yes
<freeemint> and why not https://pastebin.com/HZkeqRfD ?
<Regenaxer> This is for the new port, which is not allocated for a single-shot
<Regenaxer> ephemeral port
<freeemint> are ephemeral ports expensive?
<Regenaxer> No, just a new bind and listen
<Regenaxer> should be ok
<freeemint> Why would you not want such a port directly?
<Regenaxer> How? It is one for each GUI session (child process)
<freeemint> Sorry ... why would you not want a ephemeral port for single shot connections?
<Regenaxer> Then there *is* already a port
<Regenaxer> eg. the parent listens at 8080
<cess11_> Unnecessary doesn't have to be expensive to be cleaned out.
<Regenaxer> then a child listens at 33215
<cess11_> It is also possible to solve sessions differently than the GUI system does it, you could write your own handling of credentials and pass it through XMLHttpRequest or JS or whatever and skip the stuff with new ports and processes if you'd like.
<Regenaxer> yes, eg. using coroutines
<Regenaxer> one coroutine per session
<freeemint> the server would become single threaded then?
<Regenaxer> but it may block all ;)
<Regenaxer> single-process
Regenaxer has left #picolisp [#picolisp]
Regenaxer has joined #picolisp
<Regenaxer> :)
<freeemint> could you process the courutines with 'later?
<Regenaxer> ^D again
<Regenaxer> rather in a 'task'
<Regenaxer> wake up the right one if something arrives
<Regenaxer> But this is not efficient I think
<freeemint> but task are single process again.
<freeemint> is task a clever wrapper around wait or does it do more magic?
<Regenaxer> ah, you mean to fork processes with 'later'. Right, this would do
<Regenaxer> around select() to be exact
<Regenaxer> Also handles IPC in the background ('tell' etc)
<Regenaxer> 'task' is just a wrapper, the core mechanism is '*Run'
<Regenaxer> and handled in (wait) and (key)
<Regenaxer> and (listen) iirc
<freeemint> Could you implement task in 'wait , 'listen, ....
<freeemint> in picolisp
<Regenaxer> ?
<Regenaxer> I mean, these functions do implement it
<Regenaxer> 'task' is just a (setq *Run ...) basically
<freeemint> so run does the magic?
<freeemint> *Run
<Regenaxer> yes
<Regenaxer> well
<freeemint> it is tortoise all the way down, isn't it?
<Regenaxer> it is waitFdCEX_A which is called internally by 'key' etc.
<Regenaxer> hehe
<Regenaxer> waitFdCEX_A looks at *Run to see what to do
<Regenaxer> what to listen for, and how long to sleep
<freeemint> how often and in what pattern does pil look for inputs?
<freeemint> (observation an open shell does not eat 100% CPU for checking for inputs and time change all the time)
<Regenaxer> "often" is described in *Run, and patterns too (?)
<Regenaxer> It does not "look" at all
<Regenaxer> man 2 select
<tankf33der> now i got 0 from native
<tankf33der> its ok
<Regenaxer> Superb
<freeemint> i understood server today.
<Regenaxer> Cool!
alexshendi_ has joined #picolisp
<freeemint> I want to write a game server. (A server with an internal regular state change even if nobody "looks" at it)
<freeemint> I am unsure whar route to pick.
<freeemint> An (update-the-world-process) + (one process per client doing the processing based on the latest tick).
<Regenaxer> I would make individual processes for the independent actors
<Regenaxer> normal logged in users too
<freeemint> Or one dumb terminal per client, which hands out jobs to central job scheduler and the central job scheduler hands them out to worker processes whose number is related to core count.
<Regenaxer> The global state of the game is in DB objects
<freeemint> (one dumb terminal process per client)
<Regenaxer> yes, text based
<Regenaxer> No need for a central scheduler I think
<freeemint> When would you consider that?
<Regenaxer> Each actor operates on the state of the game (world)
<Regenaxer> Consider what?
<freeemint> under what conditions would a own scheduler make sense to you?
<Regenaxer> Pil IPC ensures that all can interact
<Regenaxer> OK, then let's see each actor a "scheduler"
<freeemint> (own scheduler as opposed to using the process scheduler)
<Regenaxer> Actor may be a human, an AI, or simple event generators
<freeemint> yes
<freeemint> (actor would be a coroutine which is passed around)
<Regenaxer> passing around coroutines can be done only within a single process
<freeemint> oh, good to know
<freeemint> not with in a family?
<Regenaxer> Probably no coroutines needed, if you have many processes
<Regenaxer> All in one family, yes
<Regenaxer> passing?
<Regenaxer> A coroutine is just a tag (symbol)
<Regenaxer> So you *may* pass it to other processes if they have the same code
<freeemint> Can i move a 'co with pilIO (or another way) between different processes of the same family?
<Regenaxer> yes, just send the whole body (a list)
<freeemint> (the success of execution ofcourse depends whether all symbols are defined the way i want it in the other process.
<Regenaxer> yes, but they will if they all (fork)ed or 'load'ed the same sources
<freeemint> exactly
<freeemint> What happens when a process has a lock on the db and is killed?
<freeemint> (or can i kill a process so softly that he dies very soon but does not cause a problem with the db?)
<Regenaxer> No problem, Unix releases locks if a process terminates
<freeemint> good
<cess11_> It is probably easier to start by developing an ontology of your world by creating and working on classes rather than abstracting it to tough decisions on game loops and world state.
<freeemint> cess11_: Point taken still i will continue that path because my ontology depends on the game loop
<cess11_> The loop is easy to change, what possibly could exist and what it possibly could do will most likely be more work.
rob_w has quit [Quit: Leaving]
<freeemint> I do not understand
<cess11_> Your loop will most likely be an imperative set of instructions. Adjusting it to fit new possibilities in the world will probably be easy compared to inventing it out of the blue and hoping it will fit your future ontology.
<cess11_> There will be many, perhaps thousands, of different types of objects. Working on them will be more time consuming and decide more about your game world than the housekeeping routines like the game loop, networking and whatnot.
<freeemint> cess11_: It will not end be a game in the classical sense
<freeemint> like i will not ned to generate much content
<freeemint> cess11_: When would you go with writing an user land scheduler over letting linux schedule the processes?
<Regenaxer> A trivial example: (unless (fork) (loop (move> (db 'nr '+Vehicle 17) 10.0 20.0) (wait 2000)))
<Regenaxer> the vehicle will move at linear speed
<Regenaxer> Other processes will always see it at its current position
<Regenaxer> No need for scheduler or explicit IPC
<freeemint> But that way two vehicles with the same speed might move differently fast
<freeemint> (one vehicle running on a higher clocked core or something)
<freeemint> Also my question is "when" is a scheduler justified in your eyes
<cess11_> freeemint: When security policies doesn't allow the general public to touch the server's innards.
<Regenaxer> No, the core is the same
<Regenaxer> it is the (wait 2000) which determines the speed
<freeemint> I might want to execute non terminating code and just kill a worker if he does not to do as the server wants
<Regenaxer> The process switching can be ignored here
aw- has quit [Quit: Leaving.]
<Regenaxer> No problem with such processes
<Regenaxer> of course you can move many objects in one process (loop)
<freeemint> i want to prioritize certain things over background tasks?
<Regenaxer> And (kill) is also not expensive
<Regenaxer> sure
<Regenaxer> But store these params in the DB objects
<Regenaxer> eg their speed
<Regenaxer> it is really trivial
<cess11_> freeemint: That is trivial. Designing what could exist and what it could do is not.
<freeemint> ok here is my point. people write their own schedulers in game engines (and use coroutines/fibers ....).
<freeemint> I assume they do that for a reason
<cess11_> Who are they?
<freeemint> good point,
<freeemint> under what reasons do you think you should build your own scheduler (mini os with lot's of domain knowledge)
<cess11_> To me it seems you don't know whether you're going to do huge space exploration stuff or a Tetris-clone, so creating a scheduler for game world events before you know whether it should update climate variables on foreign planets or update positions on a playing field would be quite hard and basically design for classes of games that aren't made and not possible to test with.
<freeemint> it is more like huge space exploration
<freeemint> or wolfram alpha
<cess11_> So what you do is kick up a pil + and build stuff until you think you like it really, really much.
alexshendi_ has quit [Ping timeout: 248 seconds]
<freeemint> sorta yes
<cess11_> Then you pick out the raisins and write it to files so you can 'load it next time.
<freeemint> roughly :D
<freeemint> occasionally i start writing in files from the beginning
<freeemint> this would be such a project
<freeemint> (allread created a file and a folder)
<Regenaxer> Important for start! :)
<Regenaxer> (picking names is always the biggest initial trouble)
<tankf33der> Regenaxer:
<freeemint> server.l
<freeemint> foldername agenda
<tankf33der> if i have list in res from getaddrinfo
<tankf33der> how to loop then?
<freeemint> tankf33der: you mean a List in C?
<Regenaxer> A list of stings?
<Regenaxer> you can get the list of strings directly from 'native'
<tankf33der> Regenaxer: how?
<Regenaxer> '(Var (1024 S S S S ...)) ?
<Regenaxer> Perhaps (Var (<size> (S . 8))) for an array of 8 strings? Not tested ...
<cess11_> I think picking names are easy, usually I just take the first character in whatever I'm doing and putting .l at the end.
<cess11_> So I end up with p.l, c.l, s.l and so on. Then when they grow and get important they get better names or split up.
<Regenaxer> :)
<cess11_> freeemint: Scheduling is in the abstract a solved problem, in Knuth, Cormen and others you'll find optimised and elegant solutions to basically any scheduling problem there is. This is because programming has been carried forward by business (e.g. ERP problems) and academics sharing mainframes/environments/resources.
<cess11_> Besides this class of problems being open for mathematically pure analysis, which also does a part.
<cess11_> Deciding how many arms or atmospheric layers there commonly needs to be on the planets in your game world is harder.
<freeemint> still i am curious under what conditions would you go with a user land scheduler (since you seem against it)
<cess11_> For me security or performance decides this and since performance usually is better when manipulating the operating system directly this usually doesn't apply when user land is a better choice, rather it is security policies determining this.
<freeemint> ok are there security policies you can enforce with a scheduler you can not enforce with picolisp and having everything i another process?
<cess11_> Yes, e.g. disallowing users to start more processes.
<Regenaxer> I think I still do not understand what you mean with "scheduler" in such a game context
<freeemint> I have a game idea and a software idea which both would profit from user land scheduling.
<tankf33der> res is list of unknown size, i need a c wrapper
<Regenaxer> But in C you must also know the size of an array
<tankf33der> loop until ai_next not nil
<tankf33der> linked list
<Regenaxer> I see, but then you can loop with 'struct' probably
<tankf33der> i will try
<cess11_> freeemint: Either code it yourself or stitch up your program to some task scheduling library with 'native. It doesn't matter until you have the rest of the application in place, then you'll know what you can discard from your scheduling system to make it better.
<freeemint> the game idea is the MOO i talked about way before, the software idea is a implementation of automatic mathematican by somebody named Lenat in PicoLisp. Both were suit themselves good to a main frame model of computing and additonal domain knowledge i have might make the application faster.
<cess11_> If you expect performance to be solved by throwing hardware at it you could just do what I recommended, write out all of the classes you can think of into an er.l. This will take much more time than you expect, especially fine tuning what should be '+Joint and what should be values in fields.
<freeemint> (got to eat)
<Regenaxer> What is that scheduler supposed to do? Schedule what?
<cess11_> Who gets access to what resource and how as well as housekeeping-stuff related to disconnects, unfinished db transactions, and so on.
<Regenaxer> Hmm, I do not see any need, in addition what is there already
<cess11_> Agreed, I'm just assuming there's reason for it.
<Regenaxer> disconnects can be done just like in misc/chat
<Regenaxer> ok
<Regenaxer> I would indeed start with misc/chat, and add a DB for the game state
<Regenaxer> Users just connect with ssh (or telnet or whatever)
<cess11_> As long as the DB stuff is well thought out and put in place one can then project it to canvas, a socket or something else depending on who's supposed to work with and enjoy it.
<beneroth> hi all
<Regenaxer> yep
<Regenaxer> He beneroth
<beneroth> so you are trying to build an optimal foundation for a building for which you don't know the size, the form, the use, nor anything much?
<Regenaxer> looks like ;)
<beneroth> good overview! https://picolisp.com/wiki/?AtMark
* beneroth goes looking what new drama unfolds at NPM this time
<beneroth> rofl
<beneroth> pastefai
<beneroth> "one user reported, "This destroyed 3 production servers after a single deploy!"
<beneroth> epic
<cess11_> Just wait until you see the Head of Engineering at Adobe showing up.
<cess11_> Oh, and notice that it took the developers almost twenty hours to discover the thread and lock it down.
<beneroth> haha
<beneroth> "most devs get that bugs happen" - yeah, I see this as a failure. bugs don't just happen.
<beneroth> that is like saying "functional" software or "features" just happen
<tankf33der> this is the struct
<beneroth> ah, linked list. I think you should also need the struct sockaddr definition.
<beneroth> I do accept it, but I still have a hard time to understand: why do people not want/try to understand what they're doing?
<beneroth> (or using)
<Regenaxer> So the struct is (I I I I N N N N), and the last N is the link
<beneroth> thx Regenaxer
<beneroth> btw my comment was obviously about NPM ecosystem and not tankf33der ;)
<Regenaxer> yeah :)
<beneroth> "Having a system test on a few docker images and checking if they are bootable post install could be an automated way to prevent a similar disaster in the future"
<beneroth> this is the solution to todays mainstream standards of doing software dev
<cess11_> Learning takes time that could be used to earn hard cash by plastering someone elses code to your employers ideas.
<cess11_> We should have murdered the people that injected ES/JS into the browser and put forth a Forth instead. That would've saved the world more than one headache.
<Regenaxer> Very true!
<freeemint> T
<beneroth> well for that this JS was invented one a single weekend it works pretty well!11
<freeemint> cess11_: Or post script for the browser
<tankf33der> struck in struct
freeemint has quit [Ping timeout: 260 seconds]
<Regenaxer> bbl
<Regenaxer> oops
<Regenaxer> moment
<tankf33der> the issue i dont see NIL or 0 in last item from struct
<tankf33der> C code returns only one item
<Regenaxer> (8 . I) ?
<tankf33der> '(R (8 . I))
<Regenaxer> not (48 (I . 4) (N . 4)) ?
<tankf33der> ?
<tankf33der> trying
<Regenaxer> is it the same R?
<tankf33der> i think so
<Regenaxer> btw, is memset needed? Structs can take ... . 0)
<Regenaxer> ie initialization bytes
<Regenaxer> no, I see, Hints is separate. OK
<Regenaxer> Is here (setq R (native "@" "malloc" 'N 8)) only 8 bytes malloc'ed enough?
<Regenaxer> I'm confused by the many R's
<Regenaxer> hmm, sorry, must go
<Regenaxer> bbl
<tankf33der> malloc tried with different numbers
<tankf33der> its so close.
<cess11_> Sorry, I've only done extremely simple things with 'native so I don't think I would be of much help.
<beneroth> same.
<tankf33der> ok
<Regenaxer> ret
orivej has quit [Ping timeout: 240 seconds]
orivej has joined #picolisp
freeemint_ has joined #picolisp
<freeemint_> Hi how do i do a list of joints?
<Regenaxer> (+List +Joint) 4
<freeemint_> (rel general (+Ref +Joint) special (+ Concept)) # Generalization of the concept (rel special (+Ref +Joint) general (+ Concept)) # Specialization
<freeemint_> like that?
<freeemint_> A list of Joints linking to Joints in a list
<Regenaxer> yes, looks good. So you can add +List on one or both sides
<Regenaxer> Ah, sorry
<freeemint_> I forgot the list
<Regenaxer> no +Ref
<freeemint_> (rel general (+List +Joint) special (+ Concept)) # Generalization of the concept (rel special (+List +Joint) general (+ Concept)) # Specaliziation
<freeemint_> Is that ok?
<Regenaxer> yep
<freeemint_> *+Concept
<Regenaxer> T
<freeemint_> And how do i interconnect them if i had two concepts?
stultulo has joined #picolisp
<Regenaxer> (put!> Concept1 'general Concept2)
<Regenaxer> (any of them)
<freeemint_> put!> handles list also?
<freeemint_> *+List
<Regenaxer> yes
<Regenaxer> It also deals with uniqueness, and fixing the opposite side
<freeemint_> explain. please
<Regenaxer> Sorry :) If you put an object which is already there, it is not put a second time into the list
<Regenaxer> and "other side" is the list in the object(s) which is/are put
<Regenaxer> Just experiment a little in the Repl
<freeemint_> Ah
<freeemint_> that makes more sense
gko_ has joined #picolisp
PurpleMessenger[ has quit [*.net *.split]
gko has quit [*.net *.split]
f8l has quit [*.net *.split]
stultulo is now known as f8l
rob_w has joined #picolisp
<Regenaxer> In fact it is not put!> or put> which handle this, but the rel> methods of the +List prefix
<Regenaxer> and/or +Joint itself
alexshendi_ has joined #picolisp
PurpleMessenger[ has joined #picolisp
alexshendi_ has quit [Read error: Connection reset by peer]
<freeemint_> Regenaxer: i want to store order sensitve list. What do i have to know?
<freeemint_> Order is like really, really important (screwing it up somewhere may break the programm) but the size is not fixed.
alexshendi_ has joined #picolisp
<Regenaxer> Sorted list?
<freeemint_> like ( a b d) and (a d b) are different and i want to make sure no method messes that up.
<freeemint_> The sorting is inherent in each object, so giving a sorting function is not possible
<freeemint_> I thought of a (list of (a list of numbers and value))
<freeemint_> Do you have anythin better?
<Regenaxer> And a, b and c are objects in the +List +Joint?
<freeemint_> Yes
<Regenaxer> Thin you simply put them as a list
<freeemint_> ok ...
<Regenaxer> (put> Obj 'var '(a b c))
<freeemint_> Maybe i am little paranoid
<freeemint_> oh that looks good
<Regenaxer> no, paranoid is good here ;)
<freeemint_> and how does +Joint know where a is jointing to
<Regenaxer> From the e/r model
mtsd has joined #picolisp
<freeemint_> right
<Regenaxer> as you did in your (rel ...)
<mtsd> Good evening everyone
<Regenaxer> Such ordering of +Joints is often done in the GUI with the +BubbleButton
<Regenaxer> Good evening mtsd!
<mtsd> Hi Regenaxer!
<Regenaxer> freeemint_, so you best first put the unordered list, to have all elements on bot sides, then do on both sides (with Obj (put> This (by '(..) sort (: lst))))
<Regenaxer> The sorting wnll not add new elements, so the opposite sides stay untouched
<freeemint_> Regenaxer: That won't work (as i intend). I want to use these tupels to introduce a notion of order on which my programm will "reason" on. There is no sorting function which is valid for more one element.
<Regenaxer> ok
mtsd has quit [Quit: Leaving]
freeemint_ has quit [Ping timeout: 260 seconds]
karswell has joined #picolisp
karswell has quit [Ping timeout: 240 seconds]