A railroad simulation using discrete event simulation
picolisp-explored.com
picolisp-explored.com
Fun fact: there is at least one track network (the Munich U-Bahn) that avoids loops and interconnecting lines "the wrong way around", so trains always face the same way (https://www-u--bahn--muenchen-de.translate.goog/fahrzeuge/?_...). Because of this, two-carriage trainsets have a "north carriage" and "south carriage", and the newer six-carriage trainsets have north and south end cars plus four middle cars. Of course, not all tracks run north-south, but the name is taken from the way the carriages point in the first (and at the moment still the only) maintenance yard in Fröttmaning.
Connect it to OSRD!
Open-Source Railway Designer (posted a few days ago) https://news.ycombinator.com/item?id=40733705
https://en.wikipedia.org/wiki/Lego_Loco
You could build little villages with train layouts. The big thing for them, is you could put tunnels that would connect to another user. It let you send trains and the mail card had "post cards" you could send messages.
I miss it.
- use ds and discrete event simulation instead of the usual dt (and continuous …)? Why? Seems all game engine offer dt …
- shall the track be one way and if two way traffic has to use 2 tracks? Otherwise if >1 train or train-group … crash ? And if there are junction … route and timing would be issues otherwise crash ?
- picolisp vs fennel ?
Not really comparable. Picolisp is really small, really simple, and that makes it kind of weird. It has fexprs rather than sexprs, there are no seatbelts (as in you will segfault when reaching out of bounds), the numbers are fixnums, the database is object oriented and you query it with logic programming, and so on.
As far as I know, no one has used it for 2D game scripting. If you want to be the first you probably can be, but it will be quite an adventure compared to Fennel.
Picolisp does have a function named 'macro', but it's not the same thing (it's more like 'string map' from tcl).
There's a lot if interesting stuff in picolisp, but the one thing I found unbearably ugly was it's use of strings ("transient symbols") as a kind of lexically scoped symbol.
'macro can be used to control evaluation, https://software-lab.de/doc/refM.html#macro , but there is no compilation in Picolisp, hence no compile time macros.
Don't see the problem with that. How is it different from using strings without quotation marks as global symbols?
It was just one of those things which bugged me ...
Choo Choo
OpenTTD allows grade separation at least but imho is inferior to Simutrans which ensures cargo and passengers have destinations and will not use your network unless you can path them to their destination.
Because OpenTTD doesn't have this it devolves into forcing cargo or people to travel the longest possible distance for the most money, instead of to where they actually want to go.
Simutrans-extended is something I'm really hyped for because the simulation of passenger desires is even more in-depth than the original.
https://simutrans-germany.com/wiki/wiki/en_extended_Passenge...
The most satisfying rail network for me is one that has both complex form and function.
In regular Simutrans, passengers want to go to a specific destination regardless of whether your network allows them to go there. This means you have to change your playstyle to fit what passengers want to do, since otherwise, people won't take trips.
I believe Simutrans handles this better. If you're someone who wants to go to Europe but the only plane ticket you can buy is to South America, you won't take that trip (Simutrans model). But in OpenTTD, passenger demand is basically "yeah I just want to go somewhere" and then you deliver them to Antarctica. Cargodist means that if you now offer service to Greenland, they'll evenly distribute themselves between Antarctica and Greenland.
I'm more of a product-minded person so I prefer Simutrans. Demand is the constant I want to optimize around; I don't want demand to be optimized around my transport network. But this also creates a much more difficult game.
One way in that vanilla Factorio is a shit simulation is that it cannot couple and decouple cars. Trains are built by hand, and then those trains remain as they are until remogrified by hand.
Want to send coal/copper/iron/something (or a combination of things) somewhere else? Cool beans! People do this every day in the real world.
In the real world, cars are often left in yards and sidings to be swapped around and loaded by switchers and other mechanisms while the locomotive that delivered these cars has departed -- probably along with a train of other cars that this station isn't interested in.
In Factorio, one lets trains fill up with things at A (according to rules), and then it goes from A to B and unload those things at B (according to rules). Stops for C, D, and E can be added, but even if they are: The whole train (including locomotives) stays coupled together together and there isn't any other way to do it.
The locomotive is always waiting unless it is travelling with the entirety of its assigned cars. The trains are completely inflexible.
Real-world train networks don't work that way. Got 50 containers to load up? Drop off 50 appropriate cars to be loaded up, and move on to the next problem while that station deals with putting containers onto cars. And at that next station, load up on already-full tankers. And then to the next station where a bunch of new Fords are dropped off in segments of TTX transport cars.
Factorio is also a shit simulation because these fixed-unit trains have a predefined route: Not only can cars not be picked up or dropped off, no station can offer things and no other station can order things. In Factorio, if there is iron to deliver: The usual method is to pick up as much iron as will fit on this inflexible train (however long that takes), and take it to station B to unload (however long that takes): It's a neat way to approximate how a belt works in Factorio over a longer distance, but it is not a simulation of how rail actually works.
It's a fun game and I love playing it, but it's not a fucking rail simulator[1]. Very few aspects of Factorio's rail system resemble actual rail systems in the real world that actually exists.
(Actually, while I'm at it: Factorio isn't a simulator of anything. Just because it is a fun game does not mean that it has to be a simulation of...anything.)
Seriously though, try it out! It's actually one of the best games I've ever played. I'd even say that it's so underrated.
It's a train tycoon game, but also silently focuses on being a real estate simulator, just like how a lot of Japanese companies don't make money primarily on train tickets, but instead real estate or other non-farebox income.
Emacs Lisp explicitly kept to dynamic binding for everything because it made for simpler overriding of functions deep in, but resulted in lower performance and various other issues, and ultimately most benefit from such shadowing seems to be focus of defadvice and the like.
Hope you can make sense of this:
>This has advantages in practical programming. It allows you to write independent code >fragments as data which can be passed to other parts of the program to be later executed >as code in that context.
This amuses me because while its technically true this amazing feat is accomplished only by denuding of the code of substantial expressive power - namely the relation between the lexical denotation of the code and its meaning. I will say this - aesthetically, I prefer picolisp's approach to Common Lisp's, which is to just paper over this problem with gensyms, packages, etc. Give me hygienic macros or give me death.
The main author does (or at least did) a lot of development on a tablet, with his own software keyboard (https://play.google.com/store/apps/details?id=de.software_la... , which I've enjoyed for years on my handhelds, in part due to the tmux-arpeggio), and his own editor (https://picolisp.com/wiki/?vip ). I think most of us do something similar, using vim or vip, maybe on larger computers, but generally a pretty minimal setup.
The REPL has string based completion, besides completing symbols it will also complete file paths. Development is heavily REPL based, you'd spend a lot more time inspecting the runtime than searching for string occurrences in files.
From the REPL you'd also read the language reference, most likely in w3m, the preferred text web browser in this community. (doc 'macro) will open the reference on this entry if you started the REPL with 'pil +', where the + is a flag denoting debug mode. You can expect the web GUI framework to work rather well in w3m.
It may be there are solutions favored in Picolisp without using macros that would be done using macros in idiomatic Common Lisp, and so those solutions don't need gensyms and whatnot.
> It is exactly the static lexical binding semantics Common Lisp which introduce the conceptual tension in macro programming that requires the programmer to manually worry about gensyms.
A dynamically scoped Lisp (like Emacs lisp by default) with those kinds of macros needs gensyms all the same. It isn't the lexical scope.
When we have (let ((x 1)) (+ x x)), then regardless of whether x is lexical or dynamic, there is a lower level of binding going on. The x in (+ x x) physically belongs to the enclosing (let ...). That is not lexical scope; it's a fact about the position of the code pieces regardless of x being lexical or dynamic.
This is why in that strategy for implementing hygienic Scheme macros that you're alluding to, syntax objects, there is a different kind of closure at play: the syntactic closure. It is not a lexical closure.
The syntactic closure doesn't say that "x is bound as a variable". Only "this x expression is meant to be enclosed in this code".
Picolisp doesn't run into hygiene issues requiring gensym because it doesn't perform macro expansion:
https://picolisp.com/wiki/?macros
If you don't have a code manipulating process that invisibly transplants pieces of code from here to there, then of course you don't have the issues which that entails.
He created a very Scheme-like language called Kernel that seemed to walk the line between lexical and dynamic in a much more controlled way.
However, what is probably more important but doesn't immediately stick out until you poke at Kernel a lot harder are the fully reified "environments". "Environments are copy on write that don't destroy older references" has very subtle consequences that seem to make dynamic scope a lot better behaved.
This also has the consequence that I can force things to explicitly evaluate in reference to the "ground environment" which is an explicit signal that it can always be compiled.
I suspect there is a lot of fertile research ground here. In addition, there is a lot of implementation subtlety that I'm not sure he really grasped. Environments need a special data structure otherwise they cons up an enormous amount of garbage (I suspect it really needs a Bitmapped Vector Trie like Clojure).
I wish Shutt were still around to talk to. :(
Either a Picolisp system is shortlived enough that your execution environment actually maps to the file your loading, or you're going to be interactively inspecting it anyway.
The only bookkeeping problem I've encountered in practice is littering the runtime with symbols. As far as I know there's no way to make it forget about symbols it has encountered, and they are interned as soon as they are encountered. I think the namespacing is supposed to counter this, but I've never learnt it properly.
I'm not sure in what situation the scoping would be a problem. The way I usually go about picolisping is using maybe a few globals (could be options, credentials, global stacks, something like that), conventionally marked with an asterisk, like *Global, and then everything else is functions that typically take one parameter, or perhaps a data parameter and a numeric limit for an iterator. Besides let-assignment inside functions variables rarely seem to be the right tool for me.
Lush, if it's the Lisp-like shell thingie, seems like a rather different programming environment, what with the inline C and whatnot. Might try it out, seems it hasn't been updated in fifteen years or so, could be an adventure.
I do see your point about picolisp being simple enough that the dynamic scope fits into the overall model; I might give it a try sometime just to see what it's like in practice.
When terminal incantations grow unwieldy I usually abstract by put them in a Picolisp function and start doing things in the REPL. It takes like ten minutes to write a wrapper around a MySQL CLI client that takes a query string and outputs a list of lists, and then you can wrap that in fork/bye and mapcar it on cuts from a list to run queries or imports in parallel. Similarly you can trivially hack up an 'Ansible at home' by running commands over SSH in parallel. If I'm worried about data races I let Linux handle it by writing lines to a file and then slurp that back in when every process is done.
Surprisingly many things are heavier or slower than launching and disbanding a POSIX process, so it's often quite viable to just hand over to forks. Once I scraped out data about many thousands of people in tens of organisations by wrapping w3m and forking on URL:s I'd gathered in a similar way. Can probably be done with Beautiful Soup too, but I already knew how to use a browser to grab a copy of a web page and low level string parsing was probably faster and easier to implement than some abstracted XML representation or XPath. The value of that data set was easily a couple of orders of magnitude larger than what I got payed to assemble it.
I mean, you can do these things in bash or Python or Clojure or something, but the Picolisp REPL has really good ergonomics and gets out of the way. Performance isn't great, but I find it good enough and have handled files with hundreds of thousands of lines without getting annoyed. Sometimes I reach for Elixir instead but it feels a bit clumsy in comparison.
Strange, I thought many Lisp systems still have source level list-based interpreters. For Common Lisp I would think: SBCL (optional), CLISP, Allegro CL, LispWorks, ECL, ... They can also compile code. Compiling Lisp code was also already available in the first Lisp implementations and having a compiler was an explicit goal of the original implementors.
Let's use the LispWorks Listener (the REPL tool):
CL-USER 25 > (defun foo (bar) (break) (print (list :hello bar)))
FOO
CL-USER 26 > (foo 10)
Break.
1 (continue) Return from break.
2 (abort) Return to top loop level 0.
Type :b for backtrace or :c <option number> to proceed.
Type :bug-form "<subject>" for a bug report template or :? for other options.
CL-USER 27 : 1 > :bq
INVOKE-DEBUGGER <- BREAK <- FOO <- EVAL <- CAPI::CAPI-TOP-LEVEL-FUNCTION <- CAPI::INTERACTIVE-PANE-TOP-LOOP
<- MP::PROCESS-SG-FUNCTION
CL-USER 28 : 1 > :n
Call to INVOKE-DEBUGGER
CL-USER 29 : 1 > :n
Call to BREAK
CL-USER 30 : 1 > :n
Interpreted call to FOO
CL-USER 31 : 1 > :lambda
(LAMBDA (BAR) (DECLARE (SYSTEM::SOURCE-LEVEL #<EQ Hash Table{0} 81D03EFF03>)) (DECLARE (LAMBDA-NAME FOO)) (BREAK) (PRINT (LIST :HELLO BAR)))
The debugger output of the currently running function looks like a linked list to me. I could modify it destructively, if I wanted.LispWorks has a list-level interpreter. One can also compile code.
Typically Lisp interpreters tend to be written in C, since that usually is more portable than Assembler.
> On the downside, this is all of course completely unchecked and pretty unsafe, and, to come back to the original point, you do not have such conveniences as lexical scoping.
I would expect from a typical Lisp interpreter (a source level list-based interpreter) that it does all kinds of runtime checks and also provides lexical scoping. If there is a clojure, then this closure would be a combination of some function and an environment. In standard Common Lisp there is no access to that environment, but I could look from an inspector into it:
CL-USER 54 > (defun example (a) (lambda () (break) (print a)))
EXAMPLE
CL-USER 55 > (example 10)
#<anonymous interpreted function 8020001A59>
CL-USER 56 > (describe *)
#<anonymous interpreted function 8020001A59> is a TYPE::INTERPRETED-FUNCTION
Code (LAMBDA NIL (BREAK) (PRINT A))
Environment ((A . 10) (#:SOURCE-LEVEL-ENVIRONMENT-MARKER FUNCTION NIL . #<EQ Hash Table{0} 81D03EFF03>) (#:FUNCTOR-MARKER LAMBDA (A) (DECLARE (SYSTEM::SOURCE-LEVEL #<EQ Hash Table{0} 81D03EFF03>)) (DECLARE (LAMBDA-NAME EXAMPLE)) (LAMBDA NIL (BREAK) (PRINT A))))
As we can see, the interpreted closure has code a list and an environment, where A = 10.I doubt that actually, though I don't have LispWorks installed to try it. Modifying a function at runtime as if it were a list is actually the best test to see if your Lisp really represents functions as lists, or as some other internal object that is rendered as a list in the REPL by accessing the stored definition. E.g. both CLISP and guile error out if I try `(car (lambda (a b) (+ a b)))`.
Another good test is to construct a function at runtime and try to call it. To do that you will probaby need to call `eval` or equivalent, just like you would in Lua or Python. Not in Picolisp though, which is why I consider it to be the only truly homoiconic programming language.
You can doubt that. But I have done it. I know that it works.
CL-USER 63 > (defun foo (a) (print 'hey) (print a))
FOO
CL-USER 64 > (foo 'jack)
HEY
JACK
JACK
CL-USER 65 > (function-lambda-expression 'foo)
(LAMBDA (A) (DECLARE (SYSTEM::SOURCE-LEVEL #<EQ Hash Table{0} 81D03EFF03>)) (DECLARE (LAMBDA-NAME FOO)) (PRINT (QUOTE HEY)) (PRINT A))
NIL
FOO
CL-USER 66 > (fifth *)
(PRINT (QUOTE HEY))
CL-USER 67 > (setf (fifth (function-lambda-expression 'foo)) '(print 'hello))
(PRINT (QUOTE HELLO))
CL-USER 68 > (foo 'jack)
HELLO
JACK
JACK
> E.g. both CLISP and guile error out if I try `(car (lambda (a b) (+ a b)))`.Sure, but in CLISP the function is still a list internally. An interpreted function is a record, which stores the code as a list internally. The internally stored list is interpreted.
Python compiles the code to byte code. CLISP has both a list-based interpreter and a byte code interpreter.
Edit: lexical analysis -> lexical scope
CL-USER 100 > (defun unroll (n exp) `(lambda () ,(loop repeat n collect exp)))
UNROLL
CL-USER 101 > (unroll 3 '(foo))
(LAMBDA NIL ((FOO) (FOO) (FOO)))
CL-USER 102 > (eval *)
#<anonymous interpreted function 8020000EC9>
CL-USER 103 > (describe *)
#<anonymous interpreted function 8020000EC9> is a TYPE::INTERPRETED-FUNCTION
CODE (LAMBDA NIL ((FOO) (FOO) (FOO)))
As you can see, the thing is basically the same as a cons cell with two entries the type and the code: (TYPE::INTERPRETED-FUNCTION . (LAMBDA NIL ((FOO) (FOO) (FOO))))
The above Lisp implementation does not use a cons cell, but a different type mechanism to easily and reliably identify the runtime type.I picolisp this is hardwired into the interpreter. The interpreter will also need to check every time at runtime, if the list structure is actually a function and what kind of function it is.
In above Lisp, the type of the function is encoded during EVAL and the check for the type is then a type tag check.
for this example here, using the LispWorks implementation, it also makes no difference for EVAL if it is a function with 10 or with 100000 subforms. The execution time is small. No special processing of the list of subforms takes place. For example the code is not compiled, not converted to byte code, not converted to another representation.
CL-USER 111 > (let ((f (unroll 10 '(foo)))) (time (eval f)))
Timing the evaluation of (EVAL F)
User time = 0.000
System time = 0.000
Elapsed time = 0.000
Allocation = 184 bytes
0 Page faults
GC time = 0.000
#<anonymous interpreted function 8020001DA9>
CL-USER 112 > (let ((f (unroll 100000 '(foo)))) (time (eval f)))
Timing the evaluation of (EVAL F)
User time = 0.000
System time = 0.000
Elapsed time = 0.000
Allocation = 184 bytes
0 Page faults
GC time = 0.000
#<anonymous interpreted function 8020000839>
CL-USER 113 > (defun unroll (n exp) `(lambda () ,(loop repeat n collect exp)))
UNROLLI always thought that `eval` in CL was an un-idiomatic and fairly expensive operation, even for code that is not compiled. You learn something every day...
So if there is another form that is actually being used for the execution, the change in source must have been detected and propagated to that other form.
Anyway, that situation looks like true blue interpreted functions. There is nested list source you can tweak, and the tweaks somehow go into effect.
An interpreted function in a Common Lisp cannot literally just be a lambda expression list, because that would not satisfy the type system. It has to be of type function and a function is not a subtype of list.
What happens is that there is some container object which says "I'm an (interpreted) function", which has slots that hold the raw source code. It might not be a lambda form; for instance, the original lambda might be destructured into parameters and body that are separately held.
There is some API by which the interpreter gets to those pieces and then it's just recursing over the nested lists.
> Another good test is to construct a function at runtime and try to call it.
Common Lisp doesn't provide a standard API for constructing an interpreted function (or even make provisions for the existence of such a thing). Lisps that have interpreted functions may expose a way for application code to construct them without having to eval a lambda expression.
It's just a matter of constructing that aforementioned container object and stuffing it with the code piece or pieces. If that is possible then that object is something you can call.
When you call that function, eval ends up used anyway.
Portability is not a concern. Either you run something 64 bit POSIX or you aren't going to use Picolisp (except if you get your hands on an old 32 bit build). I think it's usually tested by a user on OpenBSD but outside of Debian you're basically on your own.
There are like three basic data types. Fixnums, symbols and the linked list. If you do something similar to what you're showing from SBCL (or LispWorks, didn't read closely enough at first) it'll look pretty much like it does in source.
$ pil +
: (de myfun ()(prinl "yo")(prinl "world"))
: (cdr myfun)
-> ((prinl "yo") (prinl "world"))
: myfun
-> (NIL ((prinl "hey")) (prinl "world"))
: (myfun)
yo
world
-> "world"
: (set (cdr myfun) '(prinl "hey"))
-> (prinl "hey")
: myfun
-> (NIL (prinl "hey") (prinl "world"))
: (myfun)
hey
world
-> "world"
Hijacking the 'de mechanism is not something you'll do often, but looking at definitions like this you'll do a lot, and from time to time navigate it with list browsing functions.It boils down to some very simple interpreter behaviours, and after some time surprises become quite rare. I find it takes off quite a bit of cognitive load when solving non-trivial scripting tasks compared to e.g. bash or Python. Especially since 'fork and 'in/'out are so easy to work with, with the former you just pass in an executable list, '((V1 V2 Vn)(code 'here)(bye)), with the latter you get a direct no-hassle connection to POSIX pipes.
CL-USER 63 > (defun foo (a) (print 'hey) (print a))
FOO
CL-USER 64 > (foo 'jack)
HEY
JACK
JACK
CL-USER 65 > (function-lambda-expression 'foo)
(LAMBDA (A) (DECLARE (SYSTEM::SOURCE-LEVEL #<EQ Hash Table{0} 81D03EFF03>)) (DECLARE (LAMBDA-NAME FOO)) (PRINT (QUOTE HEY)) (PRINT A))
NIL
FOO
CL-USER 66 > (fifth *)
(PRINT (QUOTE HEY))
CL-USER 67 > (setf (fifth (function-lambda-expression 'foo)) '(print 'hello))
(PRINT (QUOTE HELLO))
CL-USER 68 > (foo 'jack)
HELLO
JACK
JACK (LAMBDA (A)
(DECLARE (SYSTEM::SOURCE-LEVEL #<EQ Hash Table{0} 81D03EFF03>))
(DECLARE (LAMBDA-NAME FOO))
(PRINT (QUOTE HEY))
(PRINT A))
If not, one would probably need to do a bit of fiddling to tear away the function from the symbol if one should feel a sudden urge to do so.Perhaps similar to this:
: (de myfun (D)(prinl "heyo") (prinl D))
-> myfun
: myfun
-> ((D) (prinl "heyo") (prinl D))
: (mapcar '((D) (prinl "heyo") (prinl D)) '("world"))
heyo
world
-> ("world")
: (mapcar '(NIL (prinl "hey")) '(lel))
hey
-> ("hey")
: (car myfun)
-> (D)
: (cdr myfun)
-> ((prinl "heyo") (prinl D))
: (cons (car myfun) (cdr myfun))
-> ((D) (prinl "heyo") (prinl D))
: (mapcar (cons (car myfun) (cdr myfun)) '("world"))
heyo
world
-> ("world")
Mostly I use this to get at an implementation so I can test it or a portion of it against some particular value, or just to see how something works. Most builtins are implemented in assembler and their symbols only return a pointer, but for example 'doc is implemented in Picolisp: : doc
-> ((Sym Browser) (raw T) (call (or Browser (sys "BROWSER") "w3m") (pack "file:" (and (= 47 (char (path "@"))) "//") (path (if (get Sym 'doc) (pack @ "#" Sym) "@doc/ref.html")))) (raw NIL))
: de
-> 270351
: macro
-> ("Prg" (run (fill "Prg")))
I really like the Picolisp 'match (https://software-lab.de/doc/refM.html#match ) function. What's the easiest way to do the same in Common Lisp? If it's not obvious from the examples there, it can also be used with character lists, i.e. transient symbols, i.e. strings, chopped up into a list of UTF-8 characters. It's similar to unification in logic programming, which is something Picolisp supports. CL-USER 130 > (defun myfun (d) (print "heyo") (print d))
MYFUN
CL-USER 131 > (let ((source (function-lambda-expression #'myfun)))
(mapcar (eval (list* 'lambda
(second source)
(nthcdr 4 source)))
'("world")))
"heyo"
"world"
("world")
Above can in some ways done in many Lisp implementations. It's in this form simply not widely used. For most applications it's more interesting to use macros to manipulate code, which then can be compiled to efficient code.Pattern matching is much implemented in Lisp.
I had adopted this code for a pattern matcher from a book (LISP, Winston/Horn), probably >30 years ago:
CL-USER 115 > (pmatch:match '(#$a is #$b) '(this is a test))
((B (A TEST)) (A (THIS)))
CL-USER 116 > (pmatch:match '(#$X (d #$Y) #$Z) '((a b c) (d (e f) g) h i))
((Z (H I)) (Y ((E F) G)) (X ((A B C))))
Not to say, that Picolisp isn't great for you, but it is not the only language where lists can be manipulated.Don't think I've made this claim.
Can the #$a &c. be used as variables?
: (and (match '("h" "e" "l" "l" "o" @A ~(chop "ld")) (chop "helloworld")) @A)
-> ("w" "o" "r")That's true, but it sounds a bit as if Lisp interpreters are something very unusual. The syntax and other details may differ, but the general idea of executing source code via an interpreter is very old, often implemented - similar also the idea that the code can be mutable. It's just not very fashionable, since some Lisp dialects are designed such that one wants the compiler to be able to statically check the code for various things, before runtime.
In Common Lisp we would not want to introduce variables into an outer scope by enclosed functions. One would explicitly set up a scope.
Example: this is a macro example, which creates a scope, where the matching match-variables are also Lisp variables.
CL-USER 165 > (pmatch:when-match (append (coerce "hello" 'list)
'(#$a)
(coerce "ld" 'list))
(coerce "helloworld" 'list)
(length a))
3