By the sound of things, the problem is that the variables created by one function are being modified by other functions. In that case, the solution is to use local variables instead of global variables. To do that, you just need to wrap the function body in a single let form:
(def ugh (str v)
(let x 15
(for i 0 (- (len str) 1) (set xi (* x 2))
(if (is "A" (cut str i (+ i 1)))
(set x (+ xi v))
(set x (- xi v)) i)) x))
The sets will refer to the locally created variable, not a global variable.
(= x 100000)
=> 100000
(ugh "AAAA" 1)
=> 255
x
=> 100000
The global x has not been modified, because the function created a local x variable and used that instead.
Here's a more concise version of that function:
(def ugh (str v)
(let x 15
(on c str
(= x (* x 2))
(if (is #\A c) (++ x v)
(-- x v)))
x))
and here's a version that uses no sets at all:
(def ugh (str v (o x 15))
(let x2 (* 2 x)
(if (is "" str) x
(is #\A str.0) (ugh (cut str 1) v (+ x2 v))
(ugh (cut str 1) v (- x2 v)))))
ah! I see. my rudimentary tests for (= x(...)) and probably having x set from previous iterations of code led me to believe (= x(...)) always changed the global x.
I dropped Moshe a line to see what he has to say regarding Plop's dependency on Maxima.
I've looked through the first few files and so far have only encountered fairly straight-forward common lisp. Hopefully whatever Maxima features are used will be relatively easy to implement.
Oddly, the Ruby version runs more slowly every time I run it. I think memory locality may be the problem, as the Ruby process grows each time (now at 85 MB). That's nothing compared to my MzScheme process which is now consuming 700MB. That's probably due to the inefficient way in which nwords is constructed. A bit of tuning there might work wonders for overall performance.
As rkts said, if you start specifying names of variables, then you aren't really saving many characters. The other problem with some of the suggestions is that you don't get the benefit of omitting the outer parens of an expression:
[car _]
Note that there are no parens in that function. On the other hand, most suggestions about putting the variables at the start reintroduce the parens:
[x y || (list x y)]
It would be better to do this:
[x y || list x y]
One option is to do what K does: parameters are called x, y and z by default:
[list x y z]
It's concise, but not really much different to [list _1 _2 _3] or Clojure's #(list %1 %2 %3).
I like the idea of re-purposing \ as a synonym for 'fn', as in some other languages. Personally, I've never used \ as a single-escape character in any Lisp code I've written, and all instances can be replaced with |...| anyway. But this is still not as concise as [...]
(\(x) (car x))
Perhaps using \ as an infix operator would be better:
(x)\(car x)
Pushing it further:
x\(car x)
x\y\z\(list x y z)
This means you can't use rest arguments with this notation, but then I almost never use rest arguments with anonymous functions, just as I don't use implicit progn (which you can't represent with [...] either). You would have to resort to 'fn' in these situations, which is fine because the language should be optimised for the most common case rather than the general case.
One final observation: almost all Arc functions take either one, two or three arguments, with the last one often being a rest argument. So these are the cases that need to be optimised.
almost all Arc functions take either one, two or three arguments, with the last one often being a rest argument. So these are the cases that need to be optimized
Although the other analysis is insightful, IMO, this is the most important part of your post. Although, I'm not looking to optimize all functions, just those with bodies smaller than about twice the size of the "fn (argument list)" code.
1. It depends what you mean by 'temporarily'? To change it in a local scope, you can shadow the function variable like this:
(def foo (x y) (pr x y))
(let foo (fn (y x) (foo x y)) (foo "one" "two"))
=> twoone
2. Square-bracket notation is the most concise way to partially apply a function to all but one argument:
(map [foo _ "bar"] '(1 2 3))
=> 1bar2bar3bar
Currying is a different thing to partial application and isn't supported by Arc, but it is possible to write a curry operator that can produce the curried equivalent of any function. (Note that the 'curry' operator defined by pg in ANSI Common Lisp does not actually curry the function, strictly speaking). Such an operator would have to take an argument saying how many arguments the original function had, as Arc has no way of reflecting on functions at the moment.
3. map is currently implemented so that extra list members are simply ignored:
(map + '(1 2 3) '(1 2))
=> (2 4)
It would be quite easy to define new versions of map that do what you require. For example, repeating the last element:
(def repeat-map (f . ls)
(if (pos no ls) nil
(all ~cdr ls) (list:apply f (map car ls))
(cons (apply f (map car ls))
(apply repeat-map f (map [if (~cdr _) _ (cdr _)] ls)))))
Thanks, that was what I was looking for. Unfortunately, I think I've configured something wrong in git or ssh, and cannot clone the arc repo for actually using it ;)
By temporarily, I originally meant without defining a shadow function, like you did, just reorganizing them for the single call. But that might not be easy to do in a concise, flexible way so I suppose shadow functions make the most sense.
You shouldn't need ssh just to clone the repo. This should work fine:
git clone git://github.com/nex3/arc.git
However, you can't push using that URL, so you'll end up with an un-pushable copy. Have you tried looking through all the guides? http://github.com/guides/home
To reorder arguments in a single call... it would certainly be easy to do that for binary functions:
As for the silence, it appears almkglor and stefano are busy working on hl (http://github.com/AmkG/hl/tree/master). I don't know if they still read the forum. I suspect a lot of other people are getting disheartened.
Also, until there's another release of Arc, there isn't much to talk about.
Yes, I think I've followed all of the guides. However, I keep getting the "Permission (publickey)" error. I wonder if there are more configurations like username or email address that have to be the same as what github expects?
; convert a cycle into transpositions
(def trans (cycle (o first nil))
(if (no cycle) nil
(~cdr cycle) (list:list cycle.0 first)
(cons (list cycle.0 cycle.1)
(trans (cdr cycle)
(if first first cycle.0)))))
; permute a list using a list of disjoint cycles
(def permute (cycles l)
(with (ts (apply join (map trans cycles))
ret (copy l))
(map [= (ret (- _.1 1)) (l (- _.0 1))] ts)
ret))
(permute '((1 2 3) (4 5)) '(a b c d e))
=> (c a b e d)
I also have no problem with the way pg is doing things. However, the flack he's getting is starting to reach epic proportions. Perhaps the following quote from Kent Pitman at Lisp50 explains it: "Soliciting volunteers gives critics something to do, which dilutes their passion, pacifies them, and makes them feel involved." Conversely, if they feel they have nothing to do, or their work isn't appreciated, they become critics.
I'm not sure the use of an open forum has been much of a success so far. When pg first announced the existence of Arc, he asked people to email him with suggestions. People were supposed to suggest features from other languages and then give examples of how that would make code shorter. Eventually, he posted all the replies on his web site, and although there were lots of ideas, not a single person had actually done what was asked. This forum has been better, but far from perfect. For example, has anyone has actually tried to write a fast profiler yet?
I think that if you want to offer a language to the community, and expect useful work out of it, you need to provide a structure that people can work in. For example, Perl 6 started with an 'RFC phase' and then moved on to 'Apocalypses'. Writing an RFC forces you to put a lot more thought into your ideas than you would if you were just posting to a forum. Writing the Apocalypses made sure that there was plenty of feedback on these ideas. People had something to do and knew where they stood.
So I don't think it really matters whether you release a compiler. What matters is whether people have a structure they can work in. That's why everyone wants to write libraries for Arc. Writing a library is something that one person can accomplish. It's a structure that channels people's energies towards a given end-point, and they are rewarded through people using the library in their own projects.
For Arc, the compiler was both a help and a burden. It helped because people could actually try out ideas. It was a burden because everyone started working on the compiler, not the language.
So I think community-aided design can work, but it needs to be harnessed correctly. The most important ingredient is probably high-quality feedback on what people are doing. Of course, that also requires the most effort, but no one ever said it was easy. :)