> Hmm, perhaps it should return the last arg instead, that way I can add strings in front, which seems more common than at the end.
yeah but that would be more costly. Getting the 'car of a linked list is cheap, getting the last element isn't (O(n)).
I think that is reason why it returns the car.
Instead of not returning anything, it returns something. I think this is good. this can be useful sometimes. For instance, (good) C code does that. It's rare to see a "void" function in C, better to instead return some infos, even if you don't see an obvious use of it for the moment.
But returning something should not be expensive. Therefore the 'car.
---
Jazzdev, where is the 'pr behaviour problematic? In my view, if you want it to return 'nil instead, it's basically that you don't care about, that you discard the result. Then getting the 'car changes nothing.
Anyway, akkartik is right, you can use 'disp. But be careful it can only prints one argument, and it takes an optional second one, the stream where to print the value (stdout by default), where 'pr takes any nb of args (and displays them to stdout).
Or I suppose you know this, you can do: (do (pr arg1...argN) nil).
Or even, if you're going to use it a lot: (def prnil args (apply pr args) nil)
---
In the same "please return something" philosophy, it could be good if 'each and co actually return something else than 'nil. Sometimes it was a problem for me, I used a 'each construct in a 'if test or something, and the test (obviously) failed.
'each returns the iterated table if it is called with "expr" being a table. Maybe it should return the iterated list when called with a list. (Or at least there should be coherence: the other solution is to make 'each always nil. Currently, the situation is confusing.)
Another idea is to make the iteration constructs ('for and co, also) return t. Then they could be used w/out problem in 'if. It makes sense: "the job is done" => t. But for 'each, returning "expr" makes more sense.
Another idea is to keep returning nil, but I find this poor. That's basically killing the "everything is an expression that returns something [implicitely: useful]". That's just a bit better than Scheme #<void> (which is a pure abomination).
Similarly, Anarki does this I think, and so does my ac.scm, but a minimum is to make 'system returns t if success, nil otherwise (and a better solution might be to return the actual exit code of the proc). Don't make nil the new #<void>.
---
BTW, the other day I cried when I typed "var a = 42" in my browser Javascript console, and saw no result line. An assignement is "undefined" in Javascript. No result. It's a statement, not an expression. This is just stupid. I really was disappointed. I lose some time because I wonder why there were no result line, this is stunning in a REPL. They told me Javascript is Lisp in C clothing, my ass.
Given that you already iterate through the list, it can be made cheap to get the last item:
(def prl args ;returns last arg
(let u args
(while cdr.u
(pr car.u)
(zap cdr u))
(pr car.u)))
arc> (prl 1 2 3)
1233
Even though 'u points to the list 'args, I can modify 'u to point to another part of the list, and the list itself is not modified. I thus traverse the list only once here.
Also, note that the REPL prints the return value on the same line as whatever other output. I think this is annoying. Common Lisp has a 'fresh-line procedure that prints a newline to an output-stream if and only if at least one character has been printed to that stream and the last character printed was not a newline. It would be nice to use that in the toplevel procedure.
Having written that, I looked at the PLT docs and figured out how to at least tell whether nothing has been written to an output-port since the last time you checked, and I hacked the toplevel function in ac.scm to print a newline when the expression printed something, whether or not that something ended with a newline. I'm not sure whether I like this better:
arc> "ach"
"ach"
arc> (pr "ach")
ach
"ach"
arc> (prn "ach")
ach
"ach"
Changes in ac.scm (I haven't learned to use diff, so I'll record them like this):
;Relevant part of resulting definition of tl2 in ac.scm:
(if (eqv? expr ':a)
'done
(let ((n (next-char-place))
(val (arc-eval expr)))
(if (< n (next-char-place))
(newline))
(write (ac-denil val))
;Then add this:
(define (next-char-place)
(let-values (((a b n) (port-next-location (current-output-port))))
n))
> Jazzdev, where is the 'pr behaviour problematic?
I'm writing an HTML package. I'd like it to work as follows:
(html
(body
(somesetup)
"Some text"
(pr "Some more stuff")
))
I'm experimenting with the idea that calling pr shouldn't be necessary, so the tag expansion eval's everything and if it gets back something non-nil then it pr's it. I'd like to still allow pr to work also, but since it returns non-nil then some things get output twice.
I want the user of the HTML package to use standard stuff, so having prnil is awkward. Allowing pr inside these tags isn't necessary. I'm just playing around and trying to see what feels natural to use.
Using pr to print something without changing the semantics of a functional program makes sense in theory, but in practice it doesn't see useful. Have you ever actually done it?
Hmm, doesn't each return the entire list that was being iterated over? I'll look out for an example, but one of the ways I iterate over stuff often ends up stuffing the repl terminal with huge screenfuls of output if I forget my do1 nil..
> Hmm, doesn't each return the entire list that was being iterated over?
No:
arc> (each x '(1 2 3) (prn x))
1
2
3
nil
But you're right, the iteration macros are mainly used while testing in the REPL, while it's often a code smell to see 'each or 'for in the final file, in my XP.
So yes maybe the best solution is to keep them returning nil, to not clutter the screen.
But a better one would be a better REPL that collapse the output if it gets too big. This would protect from a whole class of "mistakes". I use a web-based REPL for administrating Arc powered websites that does this (like the one at dabuttonfactory.com:8080, only better). There were a thread on HN lately about a Python shell that does this too (collapsing output), unfortunately I don't remember its name.
"Improve the tools, improve the language", and not "keep the tools crappy, make the language crappier to fit the existing tools".
Current REPLs are very stupid when you think about it, and lots could be made to improve them. I mean, my Arc REPL in a terminal is not even as good as my Unix shell REPL. And I don't consider my Unix shell REPL awesome anyway.
Rah I should find you the link about the Python shell or clean the code (but it's JS anyway :-/) and show you my web REPL. Both do a bunch of trivial things that greatly improves the usability. For instance, they work on an "expression" level, not a "line" one. If you enter a multi-lines fn definition, then hit the up arrow, the whole definition is copied back in the input area, not just the last line. Mine also works with the mouse: I can click on a previous definition to copy it back in the input area, and this is much better for some cases that having to hit <UP> 20 times. Etc, etc.
Frankly, if "dynamic development with a REPL" means fighting with rlwrap, then I can be way more productive with editing a file in Emacs and a compile stage. Compiling is not that slow nowadays. Or have a look at Golang: it can even be blazing fast. Even Slime (for Emacs) is not really that better IMO.
There are even drawbacks with a REPL: you try things in it, but when you're done trying, you have to copy/paste the code to the editor window: you lose some time you would not have wasted with a static language! Big big hacks should be done in this area. Current REPLs are not good enough. That's the reason I know no Python guys that actually use `python' as a REPL. Us Lispers use one, but I guess it's mainly because we are used to it (i.e: Lisps tutorial begin by telling you to start a REPL, Python ones to start a text editor).
The ideas of making them less awkward (i.e: don't be terminal/line-oriented) and/or usable to some extend with a mouse are just little steps (but a giant leap in usability IMO). I hope some hero will come one day and will find a way to reunify the REPL and the editor window or something. Currently it's too much of a pain. How can I say, for instance, "OK, this redef I tried here in the REPL is better than the one in the source file, replace it"?
Getting better repl tools, and language integration with the repl, are two of the main things I'm interested with in arc.
I've only gotten as far as 'src and 'ppr, but I was hoping to get to the point where arc automatically documented it's current code (i.e. the stuff actually running) and made it visible through both web and repl interfaces. In theory, 'src could be used to solve the repl -> editor -> file problem. If arc kept track of the original source files, and the current 'src associated with each of those functions, then it could possibly display the diff and even update the files if you wanted it to. Heck, why not give it a git interface, so that you can add and commit changes from the repl as well.
The reason I like arc is that the language is very easy to change, and I'm hoping to change it to make it the language that is most "aware" of itself, and its current source code.
Making a better repl is certainly something I'm interested in.
No problem here. I repeated the 'repeat loop 3 times.
$ lsb_release -a
[...]
Description: Ubuntu 9.10
Release: 9.10
Codename: karmic
$ mzscheme -v
Welcome to MzScheme v4.2.1 [3m], Copyright (c) 2004-2009 PLT Scheme Inc.
But bugs related to atomicity exist anyway, this is certain. The thread/atomicity stuff is a subtle mess.
Threads are never a solution. Message-passing / shared-nothing threads maybe, event-based maybe, something else maybe. But traditional "à la Java" threads have prouved to be a bad idea.
Why are threads in Arc, after all? For srv.arc. Which would do better w/ an event-based architecture.
And the GIL. Gosh. Python is learning the hard way how a GIL is painful and should probably be avoided from the start.
BTW:
arc> (= var 0 tbl (obj var 0))
#hash((var . 0))
arc> (repeat 50000 (thread (++ var) (++ tbl!var)))
nil
arc> var
50000
arc> tbl!var
50000
; OK, the above is normal and expected
; Now, let's sleep for a random time in each thread before to '++
arc> (= var 0 tbl (obj var 0))
#hash((var . 0))
arc> (repeat 50000 (thread (sleep:/ (rand 40) (inc:rand 50)) (++ var) (++ tbl!var)))
nil
arc> var
49817
arc> tbl!var
50047
; WTF?!
Huh I'm surprised, I knew 'assign wasn't atomic, but I thought it was OK for 'sref ('++ expands to '= which expands to a call to 'sref but in a 'atwith expression). Seems not. Is my test bogus somehow?
----
Some people, when confronted with a problem, think "I know, I will use threads." Now they have two problems.
(Running on the Ubuntu/MzScheme combo described in previous comment, and on plain vanilla Arc 3.1 (ycombinator.com/arc/arc3.1.tar). Runned ~10 times, each time the results are different but never 50000)
Yes, reproduced on ubuntu/mz4.2.4 (sorry I was making stupid mistakes last night when I tried it out). On snow leopard/mz4.2.2 the tbl!var is always at 50k, but the global var is always lower. I've seen it as low as 43866.
Here is a "plugin" example to handle the kind of "web best practises" I respect, those that make it faster for my customers.
;;; web-static.arc: module to deal with static files (CSS/JS)
; designed to leverage a reverse proxy to serve the files when in production
; currently specific to my own needs, will certainly always be.
; main requirements are: eternal caching when possible, CSS/JS
; minification, ability to correctly handle external files we can't
; easily control (think images url in CSS), ** minimal overhead **
; (i.e: code as fucking simple as fucking possible)
; CSS/JS minification: we use the YUI compressor and put the
; files in minified-dir*. nginx is told to look first
; in this directory and fallback to static-dir* if not found
; (for the files "out of control")
;
; eternal caching: we set a query string "?<mtime_of_file>", and told
; nginx to inform the client to cache this URL for 1 year
; (1 year is max allowed by RFC and anyway sufficient)
;
; (wipe testing*) to activate minification and query string.
; doesn't matter if you do this while not being actually behind nginx,
; nothing will break, httpd.arc is still serving the static files correctly
(= static-dir* "res/static/"
minified-dir* "res/minified/"
static-path* "/static/" ; URL (not filesystem) path
code-compress-prog* "yuicompress" ; sh wrapper around yuicompressor.jar
testing* t)
(def sendfile (fname (o mt (mimetype fname)))
(resphead http-ok+ (copy httpd-hds* 'Content-Type mt))
(prfile fname))
(register-path (string "/" static-path* "/*") ; never reached in production
(fn (req file)
(aif (file-exists (string static-dir* "/" file))
(sendfile it)
(resp-err))))
(def static-url (fname)
(string static-path* fname
(when no.testing*
(+ "?" (mtime (compress-ifstale (+ static-dir* "/" fname)
(+ minified-dir* "/" fname)))))))
(defs csss (fname) (css:static-url fname)
jss (fname) (js:static-url fname))
(def compress-codefile (fsrc fdest)
(ensure-dir:dirname fdest)
(system (+ code-compress-prog* " " fsrc ">" fdest)))
(def compress-ifstale (fsrc fdest)
(when (and (in (file-ext fsrc) 'js 'css)
(or (~file-exists fdest) (> (mtime fsrc) (mtime fdest))))
(compress-codefile fsrc fdest))
fdest)
(defmemo compress-code (str (o type 'js))
(w/tmpname tmpf
(w/outfile s tmpf (disp str s))
(out-from code-compress-prog* " --type " type " < " tmpf)))
(with (_ijs ijs _icss icss) ; redef web.arc ones
(defs ijs (str) (_ijs (if testing* str (compress-code str)))
icss (str) (_icss (if testing* str (compress-code str 'css))))
)
;; todo:
; * X-Accel-Redir in 'sendfile if behind nginx.
; heuristic: look if X-Real-IP present. or make the proxy pass
; a header with its name to be more correct (X-Forwarded-By)
;
; * img-compress-prog* (`optipng')?
;
; * a clean way to do the call to `yuicompress' asynchronously
;
; * gzip here to not have nginx do it on-the-fly each time? not sure
; if the gain is that valuable
;
; * 'compress-code[...] bad names?
;
; * use GG Closure compiler and not YUI, use its REST API, and therefore
; be obliged to make it asynchronous
;
; * hash instead of mtime maybe.
;
; * like for web.arc, '=once macro or init procedure so that one can use a !=
; path without having to change the file.
'mtime, 'file-ext 'mimetype are defined somewhere else. 'mtime is just calling the `stat' program via 'system. I don't have access to the file they're defined in right now (they are in a "files.arc" file) but I'll post it next week.
Obviously, using a reverse proxy makes the need of 'setuid
irrelevant (it is such a low-level syscall anyway. even plain old unix daemons should use the daemontools and don't do
this by themselves). Nginx could be made to keep-alive and gzip, which are huge perf wins. Not serving the static files by the app server is so obvious, even news.ycombinator.com does this know.
The manual "wait 30 seconds, then kill the 'slow' client" handling of srv.arc is a crappy solution (but the crappy threading model asks for it): sometimes my wifi connection is so slow, I couldn't finish a POST to this forum (yes it happened for real, I should retried each POST several times). A reverse-proxy, by buffering and handling slow clients in the good manner (i.e: not killing them brutally: if they don't write for some time, it's OK it's just an idle fd in the select() poll) removes this problem.
'page is a macro on top of 'htmlpage to create a page with the look&feel of the project site.
'login is something like: (goodcrypt pwd (get-passwd-of-user user)) ('goodcrypt is in files.arc).
"op" means "operation" in my lexicon, and is for /x/... paths, stateful actions. I know,
this is confusing, it's not the same notion than in srv.arc ('defop). But in my mind, even
when it comes to webapps, the default is statelessness and resources-oriented, not stateful
operations.
People (me the first) basically only care about "resources" (informations, content), be it a rich Ajax-full webapp or a basic HTML page, anyway.
In srv.arc, the "operations" system is +/- the 'fns* / 'fnids* / 'flink / etc. stuff.
----
The complete nginx config file for the above project site:
Gzipping and other general config directives are defined in the main /etc/nginx/nginx.conf file
and are invisibly "inherited" here.
I don't log accesses to /static/* and /favicon.ico / /robots.txt, but I do log
accesses to the rest of the website (access_log directive in the "location /" block).
A comment in web.arc mentions scheme2js, more infos here: http://www-sop.inria.fr/mimosa/scheme2js/ It's a scheme to javascript compiler, which is smart enough to substitute the TCO with the use of a while loop or a trampoline (depending of the case), and that can do a bunch of other optimizations too (like inlining calls to +).
It is used in the HOP project (http://hop.inria.fr/) which is a framework to develop rich webapps (i.e: w/ a rich javascript-backed client GUI and w/ Ajax) using Scheme for the server and the client. It is quite impressive (the website is a demo). To Thaddeus: wtf would you want to compile Arc to CoffeeScript and not to raw Javascript directly?! If you decide to write an Arc to JS compiler, be sure to check out scheme2js!
Strange, which version of mzscheme do you use (`mzscheme -v')?
'empty? is in the MzScheme base "scheme" module for me (MzScheme 4.2.1)!
Anyway, 'empty? is called only in as.scm, and you can actually leave this file unchanged for your case (I think). Just patch ac.scm.
> Also what does (asv) do instead of (load "news.arc") (nsv)?
'asv is used to start the "Appplication server" defined in "app.arc". App.arc is generic, and news.arc use/require it. I use it in the example to see if maybe it was a problem w/ news.arc specifically (and because I feel lazy about typing "(load ...)": app.arc is in the Arc "stdlib" (see the file "libs.arc"), and so is loaded by default).
'nsv is defined like this, in "news.arc":
(def nsv ((o port 8080))
[...] ; do news-specific init stuff...
(asv port)) ; relay to 'asv, which itself relays to 'serve (defined in srv.arc)
The "sv" in "asv" / "nsv" is for "serve".
> How are generally other people running it detached?
They don't: Arc is different :-D
---
Ah also, to start the news server in the background, no need of a script, you could use:
$ echo '(nsv)' | arc news.arc - &
But assuming you use a slightly improved version of the (full) patch this time:
But you still need to leave the terminal opened. To fix that, I don't know you may use some Unix daemon-ization magic. It becomes specific to your needs. Are you trying to use the news.arc forum on a (distant) server?
Once again, consider `screen'. Arc has no Unix signal handling for instance, so an "Arc daemon" would not be "powerful". The only possible interaction would be to kill it (via `kill'). Better keep an eye on it, i.e: keep a REPL opened to manage your forum.
arc> Error: "Bad object in expression #<eof>"
arc> Error: "Bad object in expression #<eof>"
...
I am using a remote vps, hence I would like to start the service and quit the server. To be honest, a web service without daemonizing does not sound very intuitive. When I say I need to stay connected to the server to keep the service alive, forums users say "Ah man, what kind of a computer engineer are you?" ;-) I think something like this must be done: http://www.itp.uzh.ch/~dpotter/howto/daemonize
Do you think if I get the latest version of mzscheme your patch it will work? What about the 15 lines offset hunk? Patch didn't seem to apply perfectly.
Edit: oh yes, maybe you use Arc 3! The current version is Arc 3.1: see the previous link. Direct download: http://ycombinator.com/arc/arc3.1.tar. The patch is to be used with the 3.1!
> To be honest, a web service without daemonizing does not sound very intuitive. When I say I need to stay connected to the server to keep the service alive, forums users say "Ah man, what kind of a computer engineer are you?"
I agree it's not intuitive, but the use of `screen' + long-lived REPL to manage the forum can actually be seen as quite smart:
$ screen arc
arc> (load "news.arc")
arc> (thread:nsv) ; start it in a new thread, to not block the REPL
arc> ^A d ; CTRL-A d, to detach the `screen'
[detached]
Your forum is started and your screen session detached. You can safely logout and exit your SSH session at this point.
At any time, you can login back to the server and do:
$ screen -r # resume
arc> ; you're in front of the forum REPL
arc> requests* ; ultra basic analytics: show the number of requests served
161803
arc> (change-some-settings) ; you get the idea
And if you need to stop the server and exit Arc:
arc> (exit) ; or just ^D if you have applied the patch
[screen is terminating]
$
One thing that is missing is there is no watchdog, no automatic restart in case of failure (I mean big failure, like: the Arc process died). Sure, it's not as solid as the daemontools (http://cr.yp.to/daemontools.html), but it should suffice. srv.arc has some kind of protection against flood, handler threads that would get mad, etc.
If you really want to just echo '(nsv)' | arc news.arc -, then use version 3.1, the patch and a small shell or Perl wrapper script to daemonize the Arc process. Some fork + redirect standard ports + setsid magic, then exec the Arc process.
In Perl, from `perldoc perlipc':
use POSIX 'setsid';
sub daemonize {
# chdir '/' or die "Can't chdir to /: $!";
open STDIN, '/dev/null' or die "Can't read /dev/null: $!";
open STDOUT, '>/dev/null'
or die "Can't write to /dev/null: $!";
defined(my $pid = fork) or die "Can't fork: $!";
exit if $pid;
setsid or die "Can't start a new session: $!";
open STDERR, '>&STDOUT' or die "Can't dup stdout: $!";
}
Thanks, I got it working. For reference, particularly your screen trick worked very well over ssh. -L option also helps if screen dies on you, it creates a log file in the current dir that you can check and see what went wrong.
Next, I believe I need to learn a bit of arc to see how I can make changes on the website.
eight looks interesting. But it took me 2 minutes to understand it was the name of the language. Maybe write it capitalized, "Eight". "eight" is quite confusing.
"Everything" is a closure, the 'leak idea, etc.: cool, interesting stuff!
pg is a well-known advocate of macros, and I suppose he influenced a lot of people in this opinion. I do like macros, but it's true, too many times you're doing it wrong if you're defining a macro.
Richard Gabriel (author of Worse is better, Lucid founder, etc.: in short: Lisp expert) said:
"Macros encourage people who are not good at
language design to do something equivalent to language design,
using tools that don't help, and with effects that are too
powerful. This makes code unreadable to people joining later and
for the authors after time has passed. Well designed macros are
well documented, but this doesn't happen much."
He makes some interesting points, but macros are like everything else that's useful (fire, nuclear power ...), they can be abused. It's fair to warn people about macros, just as people are warned repeatedly about multi-threading; but it doesn't mean macros, or multi-threading, are wrong or bad. Having a few years of practicing "Don't Repeat Yourself" in java, perhaps, you have finally hit the limit, and realise that removing more duplication means a net increase in volume of code. Arc and assembly are the only non-condescending languages I know, and I love the freedom of writing unfettered code in arc.
I notice that arc's macros are mostly very short and to the point, and are strictly hygienic even though the core language doesn't impose it. As for me, I'm still learning this discipline, just as it took me a while to understand why short methods and small classes are "better".
Interesting. I suspect I overuse macros; I try consciously to improve by constraining the surface area of the program that lies under defmacro, but there's a long way to go.
One pattern: I often used to use defmacro just to get call-by-reference. Most of the time I can get by with just primitives.
(disclaimer: I'll nitpick in this post, and give some free, personal, critiscm, but akkartik asked me for comment by email, so this is what I'll do)
> My current approach is more efficient in I/O: registries for load and save functions, and a thread that periodically saves everything.
I think my improved solution (http://arclanguage.org/item?id=10696, just below) is actually the most I/O effective. Yours will rewrite the file(s) every 10 seconds, regarless of if the content has actually been modified. But I like the global thread idea.
You're a good system programmer to write a temp file then rename it. But Arc does this for you when you call 'writefile.
This brings me to: your code is difficult to read. You're defining, IMO, way too many clones/variants of existing Arc functions ('tablist2, 'listtab2, 'fwrite, etc...). Maybe they're actually needed for your case, but maybe take the time to dive into Arc source, and see if something existing is not fitting your needs. If you really need to define them, please include a short docstring or a comment to explain why '...2 is needed.
'alist?: ahhh I'm like you, I like the '?' convention for predicates, but Arc doesn't follow it, and use the ambiguous 'a... convention instead. IMO, it's better to follow the convention, even if bad, because otherwise, again, it makes the code inconsistent, difficult to read by others. 'alist? is a name too close to the existing 'alist and this is quite confusing. fallintothis' suggestion, 'an-alist is a good one IMO. Still, I laughed reading it, because well, that's where the 'a... convention brings us. 'an-a... LOL.
Lisp is so awesome to let you use "special" characters in identifiers, and predicates are not often used, and '?' is like '=' it's easily parsable, I can't understand the use of 'a... But this is maybe just personal taste. The ...[-]p convention of CL is worse than '?' but at least less ambiguous. Why is 'afn not [isa _ 'fn]? What about the anaphoric stuff, which also use 'a... etc. And of course, english-centric convention, which is worse than latin-centric when you can choose between the two.
In general: interesting idea and implementation. Still, I prefer either 'diskvar/'disktable because it's in vanilla Arc, either my 'db/'ptable because the code is, well I wrote it so I can't objectively judge but, easier, shorter to read, and it doesn't use macros. And 'db/'ptable is transparent, where your code is like 'disktable: half-transparent (still need to 'todisk/'save-state).
Thanks palsecam, I found it most useful. The issues with 'a naming are compelling. Good to know writefile implicitly does write+rename.
I like your alternative; weird that our comments kinda crossed (sorry I missed it before). I don't yet understand how it avoids unnecessary writes. I didn't appreciate how foundational sref is.
I left a link to an earlier discussion about the ..2 variants :) (http://arclanguage.org/item?id=10677) I need read-nested-table and write-nested-table because read-table and write-table don't handle nested tables. fread/fwrite is my attempt at a unified interface for pickling arbitrary objects. Ideally read/write would take care of that.
OK, I re-read it and I can understand now. Thanks. An unified interface is a good idea.
I just know about it but can't remember its purpose, but 'load-tables (notice the final "s") exists. Maybe it's here for nested tables [edit after looking at http://arcfn.com/doc/table.html: no it's not].