If (when!) Arc becomes the 100 Year Language that we all want it to become, it's various libraries will become rich and numerous.
On the one hand, I don't want my .arc files filled with boilerplate code to load the libraries I want; on the other, I don't want a huge number of functions to be loaded that I won't actually use.
I feel that autoloading is the perfect compromise: granted, the 'autoload pointers' will have to be loaded (by default and automatically -- otherwise that's just more boilerplate) -- but this yields far less overhead than loading the actual functions.
For big programs, pg's dead right: far more of the speed will come from optimising the program -- however, for small, sysadmin-type programs, where your program is so small that your algorithm is already optimal, but the actual problem is quite large (e.g. a gargantuan directory structure), the speed comes from a tight language. (And you'll write far more of these microprograms in your lifetime than big, optimisable programs.)
Please don't get me wrong: I agree with you wholeheartedly that there should be a profiler in Arc, and that this should be used to optimise Arc --
As per akkartik's comment, we already have multi-argument anonymous functions in anarki:
([prn _1 _2] "hello " "there")
But yeah maybe having full lambda list capabilities in the anonymous function would be cool.
([((a b) c) (prn c b a)] (list "is" "name ") "my ")
So if the first argument to the bracket function is a list, it acts as a lambda list.
But this interferes with the arc special syntax of having lists act as functions on their indices. Because the following is already valid arc code:
(let x (list 1 2 3)
(x 2))
;; > 3
In that case having different delimiters might be the way to go. So your brace notation would be used for anonymous functions with full lambda list capability.
(let x (list 1 2)
({((a b) c) (prn c b a)} x 3))
Edit: trying some other formatting...
(with (f {((a b) c)
(prn c b a)}
x (list 1 2))
(f x 3))
I agree; the nested case is a bit difficult for humans to parse.
I wonder if there's a way to make (fn (x) ...) even more lightweight.
What if, to do the same thing, we had a new anonymous function syntax?
{x ...}
It would work somewhat like this:
arc> (let my-fn {arg (string "I got " arg)}
(my-fn "this thing"))
"I got this thing"
This is somewhat analogous to "let"
(let x ...)
It could also allow for multiple arguments, like this:
{(x y} ...)
Allowing for multiple arguments prevents destructuring-bind style function calls, though, which is a downside. I guess you could use other kinds of brackets for that?
{{x y} ...};; this would be non-destructuring-bind
{(x y) ...};; this is destructuring-bind
{{(x y} ...);;; this is the same as #2; it would destructuring-bind the first argument, a list.
Heck, we could even build it into the existing anonymous function syntax:
> I know Emacs Lisp has this sort of functionality -- so should Arc have something similar?
Emacs's autoloading is just about making startup faster. Autoloads are pieces of code that say "There's a function named `#'foo` in file `bar.el`". Yes, you don't load bar.el, but you do have to load `bar-autoloads.el`, which is the file that contains the autoload pointers. I'm not sure it's a win, bureaucratically. There's some documentat about Emacs autoloads here: https://www.gnu.org/software/emacs/manual/html_node/eintr/Au... .
There's a difference between included libraries and non-included libraries. You make this point pretty clearly in your post, but I saw "Emacs" and jumped to that sentence. Right now Arc loads all included libraries. Is there a particular reason you want to not do that? It seems like the main advantage is startup speed, in which case I wonder if we could just profile^1 and optimize Arc, and get better results from the same amount of work that it would take to write some sort of autoload system.
[1] pg's talked several times about profilers being vastly important^2, 3. It's a shame there isn't one in Arc.
> From very early, maybe from the begining, the Scheme spec said that conforming implementations must do tail call elimination. The first time I read this, I thought "wait, can you require this in a spec?" Arc will see this increase, and raise it by some standards for profiling.
> The way to get fast code in Arc will be to profile it and then add declarations that improve efficiency where needed.
> Users don't need benchmarks to run fast. What they need is a language that can show them what parts of their own programs need to be rewritten. That's where speed comes from in practice. So maybe it would be a net win if language implementors took half the time they would have spent doing compiler optimizations and spent it writing a good profiler instead.
I like this idea a lot. I don't like that I've gotten used to the inconvenience of importing math and re in Python. It shouldn't be that hard to use trig functions!
I briefly thought about a static analyzer that would do any necessary imports when the code is first loaded into the interpreter, but there seem too many ways that could get complicated. I agree loading a library on first use is a good way to go.
Yes, I could get behind this, with one proviso: all the code the system knows about should be under the project. Kinda like npm's node_modules/ directory. With that setup your proposal feels like a ctags for the compiler. Yes, definitely worth doing.
Considering it for about a minute, I say bracket functions of multiple args is better to have than nested bracket functions. The multiple args case applies more often I would guess. And the nested case is hard to parse, so taking away the power of the multi arg case to have the nested case doesn't seem worth it.
Interesting idea; can you think of any other uses for it?
FYI, Anarki uses _1, _2, etc. to support anonymous functions with more than one argument. It might get confusing to have both. So which would you choose?
They key quote that got me interested in it: "..we believe it is important to clear up the longstanding confusion about the source of LISP's metalinguistic power -- contrary to folklore it is unrelated to the fact that LISP programs are represented by lists."
>If you are going to get into programming Lisp with Emacs, you should look into Evil (vim bindings for Emacs), paredit (smart paren editing), ac-slime (autocomplete for slime), show-paren-mode (shows matching parens), and undo-tree (a better version of undo/redo).
Yes, customizing Emacs is really useful for making it better to use. To help with that, here are some of my config's settings for things you've mentioned. My show-paren-mode settings are here (https://bitbucket.org/zck/.emacs.d/src/default/init.el?filev...).
Instead of paredit, I use smartparens. They do similar things, but when I looked at the two, I thought smartparens was better, although I can't remember why right now. My config is here (https://bitbucket.org/zck/.emacs.d/src/default/init.el?filev...).
I should similarly check out the other things you've mentioned (except Evil, 'cause I don't like modal editing).
and then whenever you open a file that ends in .lisp, it will enable slime-mode.
If you are going to get into programming Lisp with Emacs, you should look into Evil (vim bindings for Emacs), paredit (smart paren editing), ac-slime (autocomplete for slime), show-paren-mode (shows matching parens), and undo-tree (a better version of undo/redo). Although I've never used it, you might want to look at Spacemacs which is supposed to supply sane defaults for Emacs.
I've gotten Slime working, but so far I'm still doing the same things I would do in an interactive session:
$ emacs
M-x slime RET
# in the slime buffer
* (ql:quickload :clamp)
* (in-package :clamp)
* (use-syntax :clamp)
Is that what do you typically do?
Also, when I try to C-x C-e an arc expression in some random buffer after the above steps, it doesn't seem to remember the above commands anymore. The only thing that works is running commands at the repl.
Reader macros work on a per file basis so I don't think there is a way to set the proper syntax from a separate file.
You might want to consider trying to setup Slime, as it provides some really amazing Smalltalk-esque features. I detailed most of them in my blog series, Debugging Lisp[0]. Some of the ones I covered in the post are: recompilation of code at runtime, restarting of a stack frame at runtime, an object inspector, interactive restarts (restarts are a better form of try/catch), various forms of function lookup (e.g. list all functions who call function X). I haven't yet covered it, but I eventually want to, is slime-macrostep[1]. It lets you interactively expand parts of a macro step by step.
Woohoo! I'll send you a pull request. Is there some way we can provide an "Arc repl" that's already in the right package and uses the right syntax? I tried this, but it didn't work:
No. It's just that asdf >=3.1.2 will check the ~/common-lisp directory by default. Prior to 3.1.2, you had to manually specify the directories you wanted asdf to check.
It looks like the version of sbcl you are using is old enough that the asdf version it comes with is older than 3.1.2. From that link, there is https://common-lisp.net/project/asdf/asdf.lisp Copy paste that into a file and add `(load "/foo/bar/asdf.lisp")` to your .sbclrc. I believe that Quicklisp comes with a more recent version of asdf, so you can also try setting that up and seeing if that configures asdf properly.
> I also tried various incantations from these tabs I have open
asdf-install was the previous version of Quicklisp, but you should now be using Quicklisp instead.
I should spend the time to make Clamp a Quicklisp package so that you would be able to install it with just `(ql:quickload :clamp)` instead of needing to deal with asdf.
Interesting! This is precisely what you need to record in the Readme!! "You need asdf 3.1 or higher." I assume you mean 3.1, right? According to http://www.cliki.net/ASDF the current version is 3.1. Is that really such a big difference?
Common Lisp's crap documentation is culturally recursively turtles all the way down, just like all the good things about it.. :/ This is why I'm rooting for you to break out of this cycle of suck and improve things. Would you consider starting with a pristine Ubuntu 14.04 without sbcl or anything on it, and saving all the commands you type in until you get to a working Clamp? That would be wonderful to include in the Readme.
Another option might be to eschew asdf since the default versions on the most common Ubuntu release interact so poorly. Just dump all notion of dependencies and directly include all the code you need in the appropriate relative paths. Give me a single "sbcl --load" command to rule them all, just like Arc 3.1's classic "racket -f as.scm".
(It's not just Common Lisp, by the way. I have yet to meet a language-specific package manager that didn't suck. Pip, RubyGems, CPAN, it's all utter garbage. NPM hasn't started to suck yet, but I'm constantly looking for signs of incipient over-complexification.)
Edit: I'd be happy to provide a temporary Ubuntu server on Linode or something for this work.
Still no luck, sorry. I'm on Ubuntu 14.04 as well, and I'm running sbcl 1.1.14.debian. I did the following:
$ sudo apt-get install emacs
$ emacs --version
GNU Emacs 24.3.1
$ cat .emacs
; from http://melpa.org/#/getting-started
(require 'package) ;; You might already have this line
(add-to-list 'package-archives
'("melpa" . "https://melpa.org/packages/"))
(when (< emacs-major-version 24)
;; For important compatibility libraries like cl-lib
(add-to-list 'package-archives '("gnu" . "http://elpa.gnu.org/packages/")))
(package-initialize) ;; You might already have this line
; from https://www.common-lisp.net/project/slime/doc/html/Installation.html
(setq inferior-lisp-program "/usr/bin/sbcl")
$ pwd
/home/akkartik
$ ls common-lisp
Clamp/
$ emacs
M-x package-install RET slime RET
# some errors
M-x slime
# seemed to work
* (require :clamp)
Don't know how to REQUIRE CLAMP.
[Condition of type SB-INT:EXTENSION-FAILURE]
I think I might be missing some step in between that you implicitly have..