An interesting article is "The Origins of the Turing Thesis Myth", which explains why it's a myth that a Turing machine can do anything a computer can.
The quick summary is that Turing machines can compute any algorithmic function. However,real computers do more than computing functions, and today's applications cannot be modeled by Turing Machines.
(withs (i 3
x (if (> 3 5) 5 1))
... insert code here ....)
Conditionals in arc (and all lisps) are expressions; so use it as a value to assign to a variable, rather than using a conditional to wrap a conceptual "assignment statement".
Documentation for saferead (from "arc.arc")
[fn] (saferead arg)
Reads an expression, blocking any errors.
Use (quit) to quit, (tl) to return here after an interrupt.
arc>
I vote for building real applications and letting those drive the requirements of the language. Only that way can we have a truly minimalist language (if that's still a goal) - everything in the language is required by at least one application. Even performance - yes, I would love some, thanks - isn't so strictly necessary until we build apps that really need it. And then we get performance in the areas that actually matter.
From the itch-theory of open source: pg had an itch for a news forum, I have an itch for a personal-finance-management system, an app to tell me who's borrowed my books, a dedicated arc IDE (@cchooper: I'm trying that in rainbow btw, it's a lot of fun). Oh, and tetris.
I don't have an itch for an arc-xml converter, or an arc sql library, or for a better arc security model or sandbox features. But such libraries might well be important for an application we write, and that would be a good time to implement them ...
The reasons I use Arc (well, not these days, due to incredible lack of time, but...) :
- excellent macro system (even if mzscheme seems to have a "dirty" defmacro too),
- the empty list is the false boolean,
- the syntax (particularily [] and :) is really a great improvement,
- I never built webapps that fast (and with so much fun),
- my list is close to almkglor's, but he's before me in the leaders' list, and I'll never catch him, so I can't follow him on this point :)
Now, sure, PLT is an excellent language/environment. But it's waaaaay older, that helps. Actually, if today I was given the opportunity to program in a Lisp of my choice (for work I mean, not for fun), I'd use Arc for a basic webapp and mzscheme for almost everything else.
Any special exception-handling would look like this:
(on-err
(fn ()
#|do stuff...|#)
(fn (e)
(if (isa e 'MyExceptionType)
(let reason (rep e)
#|handle according to reason|#)
; unknown error, rethrow:
(err e))))
In fact, I have a draft implementation of 'on-err and 'err for arc2c:
(set on-err
(fn (f fh)
(ccc
(fn (k)
; let tmp (%curr-err)
((fn (tmp)
; set up a continuation guard
(%cont-guard-up)
(%set-err
(fn (e)
; tear down continuation guard
; and current error handler
(%cont-guard-down)
(%set-err tmp)
; so that if fh throws, it throws
; on the original err instead of
; recursing
(k (fh e)) ))
(f)
; tear down continuation guard and
; reset error handler
(%cont-guard-down)
(%set-err tmp))
(%curr-err))))))
(set err
(fn (e)
((%curr-err) e)))
(%set-err
(fn (e)
(%pr "Error of type: ")
(%prn (%type e))
(%pr "Error: ")
(%prn (%rep e))
(%halt)))
%curr-err and %set-err would have to set a thread-local variable in the C-side. %cont-guard-up and %cont-guard-down just need to set up continuation guards.
The problem lies in the split between how Arc and mzscheme represent lists. Arc essentially uses iso to compare table keys (iso compares lists by structure, so while (isnt (list 'cons) (list 'cons)), (iso (list 'cons) (list 'cons))). Thus both your examples should work.
The problem lies in the definition of lists. (I'm explaining this from the beginning because I don't know what your skill level is; if you know how lists work, then please skip the rest of this paragraph.) Lists are composed of cons cells, each of which is essentially an ordered pair of a car and a cdr. The car holds the value, and in a list, the cdr must be another list. So what do you use to represent the empty list?
This is where the problem lies: in mzscheme, () is the empty list, and 'nil is just a symbol. In Arc, nil is a symbol that is also the empty list. Thus, we get
Note that the two keys are different: the first key is a Scheme list, ending in (), whereas the second is an Arc list, ending in nil. What's happening becomes clearer when we look at the definition of list (Anarki docstring omitted for clarity):
(def list args
args)
So as far as I understand it, when an Arc function with a variadic parameter (like list) is called, the arguments are passed to it as a Scheme list instead of an Arc list. Usually this is transparent, since Arc tries to convert () to nil on the Arc side, and nil to () on the mzscheme side.[1] However, it's not perfect, and here's one of the places where that shows up.
I didn't see an easy fix inside ac.scm, and don't have the time to keep looking. However, now I'm worried that that's going to trip me up sometime in the future... The only solution I can think of is to explicitly call ac.scm's ac-nil function, which performs the translation from () to nil, but this is (a) very ugly, and (b) only works on Anarki. For what it's worth, though, here it is (where $ is Anarki's macro for accessing mzscheme):
...yes, this works! Well, almost. I didn't really test it, but (a) ordinary things seem to get evaluated fine, and (b) the message is printed. It doesn't seem to hit everything in a macro-expansion, but I can't tell whether or not it should. For instance, I get (output reformatted for clarity)
. (Of course, its output didn't have extra line breaks and had quasiquote instead of `, etc.) I feel like it should print
- debug - (do (prn 1) (prn 2) (+ 1 2))
too.
<EDIT>
The problem is that ac-mac-call calls ac directly so that it can pass the current environment as a second argument. Perhaps arc-eval should take an optional environment argument too? But that would clutter things unnecessarily on the Arc side... anyway, the point is, after looking at it for all of five minutes, macros are tricky. (And I haven't even looked at ac-macex yet...) For instance, a simple test:
arc> (nil 1 2)
Error: "string-ref: index 0 out of range for empty string"
arc> (redef eval (expr)
(if (and (cons? expr) (~car expr))
(cdr expr)
(old expr)))
#<procedure: eval>
arc> (nil 1 2)
(1 2)
arc> (mac donil (a b) `(nil ,a ,b))
#3(tagged mac #<procedure>)
arc> (donil 1 2) ; Should return (1 2), but:
Error: "string-ref: index 0 out of range for empty string"
</EDIT>
The downside is that, if it isn't slow now, it might well prohibit certain optimizations (though reading Steve Yegge's recent talk makes me think that I might well be wrong). Is this worth pushing on Anarki?
That's not quite true. You would not see Lisp code written as though it were assembly, full of gotos and working with a finite number of registers, to take an extreme example. Lisp is a multiparadigm language, but it is heavily functional. As such, writing imperative code is not generally advisable ("not in the spirit of Lisp"), but is possible. Similarly, code with such a heavy use of variables is likely unfunctional, and thus would similarly be outside the "spirit of Lisp". Yes, you can do anything you want in Lisp (cf. the Church-Turing thesis), but it may not be advisable ("in the spirit of Lisp"). Again, for instance, if you want to work in a stack-based manner, it would probably be advisable to either (1) rework your code, or (2) switch to a stack-based language like Factor.
This is not unique to Lisp: if you want to write a tail-recursive, functional, list-based program in C, that's probably not a good idea. You can, but since C is optimized for iteration (with many implementations not even supporting tail call elimination) and for imperative programming (it lacks closures and anonymous functions), and since memory management in C is…clunky…you would be better off (1) reworking your code, or (2) switching to a more functional language with lists and tail call elimination, such as a Lisp.
In short, yes, as a Turing-complete language, Lisp can do anything. And as a multi-paradigm language (with macros), it can do a good job at performing a given task in any way. But it has strengths, inclinations, and intentions, which together do comprise what could be called a "spirit of Lisp".
case ContOp.ApplyFun: // exp2 is a function.
[exp, env] = applyFunction(exp2, args, k, env);
if (exp instanceof Promise)
exp = await exp;
break;
This means the web page is still interactive during the evaluation effectively.
Here is an example: https://nukata.github.io/little-scheme-in-typescript/example.
Click the "Load" button twice and you will see two "yin-yang puzzle" threads run on the page.
Click the "Stop at Writing" button twice to stop them.
"In `acond`, `aif` and `awhen`, `%test` or `%t` gets replaced with the 'test' form. `%then` gets replaced by the 'then' form, and `%else` by the 'else' form."
"If nested you can access the previous level by doubling the first letter of the symbol. For example, '%ttest' would get you the previous [containing?] 'test' form, while '%eeelse' would get you the 'else' form 2 levels up. In the `aand` and `aor` macros you can reference arguments by using a symbol of form `<star><num>` where 'num' is the 1-index of the argument. Previous levels are accessed by doubling the `<star>` character. So the second 'test' form of an `aand` can by accessed with `<star>2` and the third argument of the previous [containing?] `aand` would be `<star><star>3`." [Working around the crappy pseudomarkdown here.]
Given the open nature of the anarki repo, it's likely that news will break. And when it does we wouldn't be able to discuss it.
So unless these tests could prove that the forum would work (which is highly unlikely) then my vote would be not to do this. It's akin to putting the services issue logging/tracking system under the same service [1]. It's a bad idea IMO.
Over the past week, I set up some HTML generation code and a deployment script on Anarki that creates this documentation page based on Anarki's (help ...) information.
There are several more things I should explain about this:
-
===== What information a help entry is based on =====
Sometimes, some help information is populated while other information isn't available. For instance, right now `list` has everything help.arc is designed to display except for a docstring:
arc> (help list)
[fn] (list . args)
list is not documented.
Examples:
arc> (list 1 2 3)
(1 2 3)
arc> (list "a" '(1 2) 3)
(a (1 2) 3)
nil
arc> (src list)
(from "arc.arc")
(def list args args)
nil
It has a signature ('args), an implementation definer ('def), an implementation body ('args), a source file ("arc.arc"), and examples ('((list 1 2 3) (1 2 3) (list "a" '(1 2) 3) ("a" (1 2) 3))), and of course a value that has a type ('fn), but it has no docstring.
When the code in build-web-help.arc generates the HTML page, it determines whether to display a help entry purely by whether it has a docstring or not. I figure this is a good way to distinguish between things that are interesting to read about and things that are idiosyncratic helper functions, but one consequence is that there is no entry displayed for `list` right now.
-
===== Broken links =====
When the documentation refers to another entry by [[foo]], it's converted to an link to the relevant entry on the page. If there is no entry by that name on the page, it's instead converted to a span of style "broken-link", which shows up in red. If you'd like to fill in gaps in the documentation, you can view the source of the page to find all the occurrences of "broken-link".
-
===== Security of repository access privileges =====
The script pushes to Anarki's gh-pages repo using a personal access token for my GitHub machine user, rocketniabot. I just created rocketniabot for this purpose. The token is limited to pushing to rocketniabot's public repos, and right now the only public repo rocketniabot has access to is Anarki. If this changes in the future, I might want to ask someone else if they can use an access token. (And if no one wants to volunteer one, it's not the end of the world; we'll just stop having automatic pushes to the `gh-pages` branch until it's fixed again.)
The token is not committed to the repo; it's set up in the Travis CI settings as a so-called "encrypted environment variable," which is only exposed during a non-pull-request build or a pull request build that comes from another branch in the same repo. I believe this prevents non-contributors from accessing the token.
Although people could make rocketniabot look bad by having it push abusive content to the `gh-pages` branch, they would first have to either push a build script that exposes the token or push content like that to the `master` branch themselves.
If we notice either of those kinds of abuse occurring, I recommend we take two actions:
- Remove that contributor from the Anarki project on GitHub so they can't keep doing this.
- Please let me know so I can revoke the compromised rocketniabot access token.
- Until I do that, remove the rocketniabot contributor from the Anarki project.
- If you'd like to set up automatic `gh-pages` pushes again and I'm not responding to messages, then I recommend you choose another user account who's willing to be responsible for the automatic pushes, have that account set up a personal access token with public repo access, and put that token in the Travis CI configuration instead. (If you do this, please change the variable name so it's not "ROCKETNIABOT_GH_TOKEN". I think we need to keep track of whose it is so we can notify the right person when it's compromised.)
-
===== Security of the website's client-side data =====
If we ever have any page on the arclanguage.github.io domain store client-side data (like localStorage entries or cookies), someone could potentially access this information and take advantage of it before they're removed from the project. Because of this, I recommend we don't store any client-side data on that domain.
musk_fan, your initial attempt inspired me to build on it :) Now that we can enumerate from a start number to an end, I hanker after something more comprehensive. Also, after reading malisper's description of iterate at http://malisper.me/loops-in-lisp-part-3-iterate, I thought I'd try to mimic its syntax, in hopes that it'll fit better with a Lisp and be extensible.
I've recently discovered the Caddy server (https://caddyserver.com/), which makes SSL and application proxy deployments super easy. Like, 2 lines of config:
domain.com #uses the domain to automatically set up SSL with Let's Encrypt
proxy / localhost:8080 #redirect everything to Arc on 8080
I will say that Arc runs a bit resource intensive, and the slightly slow boot times mean you don't want it to have to re-launch because of infrequent requests. I don't know how well it would work on Heroku.
Also, some VPS services like vultr.com offer $5/mo nodes that have more resources than what you get from Heroku at $7/mo anyway.
My first thought is a package system based on melpa / use-package from emacs. Please point out if something like this has already been done and I have just forgotten it.
Basically, just a simple index and package fetching system that pulls libraries directly from github or other vcs sources. Then we only need one file in the standard library "package.arc", that provides functions for querying the index and fetching packages from github into a ~/.arc directory, supported by a macro like use-package from emacs (https://github.com/jwiegley/use-package) or 'ns from clojure.
An important point here is that a package fetching utility can be independent of any module system. Which is good, because we don't really have that yet. The emacs community doesn't seem to think one is necessary; everything is just imported into the global namespace, and prefixed with the package name if necessary to keep it separate. Maybe we could make some macros to simplify the prefix process, but that could quickly get complicated.
We could also experiment with some avant garde packaging ideas, such as akkartik's thoughts on avoiding version pinning, searching the vcs sources directly for the package, or building the community CI tools that automatically find downstream dependencies and run their tests against your lib changes.
BTW I'm thaddeus. I check in once in a blue moon, but decided to vote on this and forgot my password so I created another account;).
And it looks as though, because I created the account seconds before voting, I failed at least one test in 'legit-user':
new-karma-threshold* 2
It's possible I failed new-age-threshold* too, but I wasn't all that interested in investigating further.
I dunno; I understand the reasoning, but it still seems like a bad design choice. I'd much rather, circumstantially, be put through a better legit-user test on account creation than to see a forum introduction like that. Oh well, the odds are low for a new user to vote on a poll as a first action anyway. I just seem to always beat the odds :) haha.
It's reading the invalid sequence as � U+FFFD REPLACEMENT CHARACTER, which translates back to UTF-8 as EF BF BD (as we can see in the actual results above). The replacement character is what Unicode offers for use as a placeholder for corrupt sequences in encoded Unicode text, just like the way it's being used here.