Hi Dan! Thanks! Yeah I tried counting spaces like we do for python indent, but that's probably not worth the trouble.
I can't take credit for the whitespace idea. rocketnia pointed out that a project called merd first came up with it (http://arclanguage.org/item?id=16724).
And yes I considered going right-associative. In fact, I originally meant to make it right-associative, but ended up showing my C roots :) I might still revisit this decision. Can you point me at right-associativity's advantages?
A good reason to make something right-associative is if it takes a Foo and a Bar and gives you a Bar in return. In this case, left-associative grouping would only lead to type errors.
Taking a look at Haskell's right-associative operators, the cons operator (:) seems to follow that rule, and others seem to take into account other idiosyncrasies. Here are my guesses for each operator:
Exponentiation to the power of a natural number (a ^ b ^ c), an integer (a ^^ b ^^ c), or a float (a b c) is right-associative, probably to match the parentheses-less math notation: (a (b c)).
The function type constructor (a -> b -> result) is right-associative (a -> (b -> result)) so that it's easy to use multi-parameter curried functions. For the same reason, function currying/application (func a b) is left-associative ((func a) b).
Low-precedence function application (a $ b $ c) is right-associative. This might be because (a b c) tends to be sufficient for passing the usual curried parameters, so (a $ b $ c) is reserved for cases where the output of each ($) isn't another function to apply. If this is true, ($) usually takes a function and a non-function and returns a non-function, so it's naturally right-associative.
Haskell has a few other right-associative operators out of the box, but I'm not sure why. In some cases, maybe it helps with perfomance.
Yeah I've played with this idea too. Wart's def and Anarki's defgeneric support overloading by type, but the original common lisp version of wart also supported overloading by number of args:
This is different from your idea and more like java. Or more like ruby, rather; functions are open definitions and you can add clauses to them whenever you want.
shader, fallintothis, will you email me? Email in profile. I'm going to post my solution to infix :) in a few days or weeks, and I would like to ping y'all to make sure I can get your criticisms.
(I'm not sure if you come here often or go periods without visiting.)
I can relate with wanting to be more accessible to non-lispers, but I primarily want to search for better ways to write programs. I'm not in the education business. I'm in the invention business.
Parsing indentation is a muscle just like parsing parentheses. Some programmers have experience with one, some have trained their visual cortex with the other. Whichever side you're on, it's good to leave one's comfort zone. Otherwise we risk being ruled by our languages and tools.
---
Checking for the space between function name and its arglist is a (minor) burden for the writer, but it seems painless for the reader. I think that's the right kind of decision. I'm happy to put the writer through some hoops if it makes reading easier.
The primary problem with Java's verbosity isn't that it's too much trouble to type. It is that it leaves readers to sip at the codebase through a straw.
Again, I'm not a fan of readable's specific approach. But I am totally on board with the general idea of looking to improve on s-expressions. I'm sure there can be new features out there that are better for existing Lisp coders.
I do agree that s-expressions shouldn't be set in stone. But I'm not sure that these specific suggestions are useful. Maybe I'm being a Luddite in regards to change.
I guess I'm on board with the curly-infix expressions for math. Maybe. I do hate having to mentally parse through lines like
{{1 * 2} + {3 / 4}}
To get "oh, this is adding two things together". But ok. Maybe. But the minute I have to distinguish between the form
... fun(arg1 arg2 arg3) ...
and
... fun (arg1 arg2 arg3) ...
I'm done. Granted, I haven't actually used neoteric expressions -- and I should try them -- but until I do, this style looks much less readable to me. I disagree that it's painless; it seems actually quite painful to me. It sure is closer to Algol-style coding, but it's just asking you to confuse the two. And that's easily done.
Fair enough. You don't typically see the no space style in lisp, so it stands out for me. But you're right that it's not as cut and dried as I thought.
The readable/sweetexpr project is probably the most comprehensive place to see the state of the art. I'm not actually eager to use it, though; I think you can do better if you don't care about supporting existing lisps.
The original authors haven't released updates in a while, but there's maybe a dozen of us here still using it, answering questions, riffing on programming language design ideas. Old-timers sometimes leave, but somehow there's newcomers to take their place.
I hope to, at some point. Unfortunately I'm nowhere in the range of riffin' on language design issues, and have a lot of catching up to do. Definitely very passionate about technology but I lost some years (20+ years to be precise) working in the civil service, including my free time in at least ten of those years which I thoroughly buried on the golf course. ;)
Yes, I got my handicap down to 10 or 11, which was nice, but what would be nicer would be getting those 20 years back and putting them to use leveling up on my coding skills!!! Still, those twenty years weren't entirely lost - I did manage to pick up a fair amount of experience writing hobby desktop applications and honed my SQL skills to a decent level. But I would like to have learned much more than that! I'm working on it now... finally.
I recall listening to an interview with Uncle Bob Martin in which he said he just figured he loves coding so much, he'll just continue doing it till they find him with his nose between the keyboards. I guess he meant it like how a cowboy means it when he says he wants to die with his boots on. I just want to echo those sentiments. And I get the sense from Arc Forum regulars that you all feel that way as well. That's why I like it here. :)
Yep :) No pressure, but metaphors like 'leveling up' and 'handicap' are just that -- metaphors. Just build something that scratches an itch, whether it's here or on the golf course. You might even be building something already. Just throw it over the wall. We're all nice here, so there's no downside compared to not showing, and unlimited potential upside in terms of getting feedback, more ideas, accelerating learning, and just getting an endorphin rush from having done something :) It doesn't have to be hacking on internals or anything. It doesn't have to be in arc or any lisp. Any little baby program will do.
One way to think about it: this is a small enough community that we can afford to get to know each other better. That's precious given the world we live in, the tendency to move fast, be busy, not know neighbors. And the best way to accelerate that process here is by sharing our code. (Or rants, but those can be harder to write and put out there, at least for me :)
---
You know, I don't know if I'm passionate about technology. I just care about code, man. Code and the using of it to make sense of how the world works.
Thanks zck and akkartik. And I especially like this: One way to think about it: this is a small enough community that we can afford to get to know each other better. That's precious given the world we live in, the tendency to move fast, be busy, not know neighbors. And the best way to accelerate that process here is by sharing our code.
I'm a computer hobbyist and am (or was) an economist and business major by training. I spent most of my coding time in the past in proprietary languages and environments (dBase, Visual FoxPro, Paradox, Visual Basic, C# and MS SQL Server), with a little bit of MySQL and PHP thrown in the mix. That was from about 20 to about 8-10 years ago. It was only in the past 2-3 years or so that I opened my eyes to the open source world, stupid me. :)
I spent the early parts of this current stage learning Python, which I liked. And sometime earlier this year I moved on to Ruby, which I liked more. It was when I moved to Ruby that I realized I didn't really like Python's mandatory white space rules (among other things), and later, after I'd got into the innards of Arc, I got to see Steve Yegge's point that Ruby also has some of the whale gut strewn around as a result of borrowing too much from Perl.
I find Arc to be a really clean implementation of Lisp, producing clear, tight code - but two things draw me back. One is the smallness of the Arc ecosystem (primarily libraries, community developed toolkits, etc) and the other is the apparent neglect by its developers. So I made a second stab at Clojure (my first attempt cooled off after facing initial difficulties figuring out the Clojure tool chain, set up requirements etc. which seemed rather off-putting then).
The second attempt was good, and I've come to appreciate the simplicity and beauty of Clojure, and the relentless effort that Rich Hickey and others like Stuart Halloway are making in evolving it further, as well as getting their design philosophy and message out to the wider community. I've caught the Clojure bug and have settled on using Clojure and Clojurescript to scratch my itches. I'm also assessing Datomic to handle my data requirements. Let's see how it turns out - I'll post reports of my evolution in this direction as I move along.
I sincerely hope that Paul Graham and Robert Morris will give a little more attention to Arc's development in the near future. It's hard to see Arc become a 100 year language without some kind of nourishment from its authors. Of course, I will continue to play with Arc, will continue to enjoy the thoughts of fellow regulars on the Arc Forum, and will do whatever I can to help maintain http://sites.google.com/site/arclanguagewiki.
The point of functions (def) is to take evaluated arguments as input and spit back a result as output.
The point of macros (mac) is to take unevaluated arguments as input and spit back a list which will then be evaluated.
---
The reason for this is to allow us to do things we cannot do with functions. For instance, consider the "let" macro:
(mac let (x y . body)
`((fn (,x) ,@body) ,y))
Now you can call it like so:
(let a 5 a)
Which returns 5. Now, what if we replace "mac" with "def"?
(def let (x y . body)
`((fn (,x) ,@body) ,y))
If we now call (let a 5 a) it will throw the error "the variable a is undefined". Why? Because functions evaluate all of their arguments before calling the function. Which means that it tries to evaluate the argument "a", but that variable isn't defined.
But macros don't evaluate their arguments, which is why "let" works.
To expand on this further, there's also a thing known as "vau" which are basically just functions that don't evaluate their arguments. If Arc had vaus, you might write "let" like this:
(vau let (x y . body) env
(eval `((fn (,x) ,@body) ,y) env))
Notice that this is exactly like the macro version, except that it has an extra argument "env" and it calls eval explicitly. Every macro can be easily rewritten into a vau, and in fact you can even implement "mac" using vau:
(vau mac (name args . body) env
(w/uniq e
(eval `(vau ,name ,args ,e
(eval (do ,@body) ,e))
env)))
What's the difference between vau and mac, then? They both receive their arguments unevaluated, but:
1) Vaus also receive an extra environment argument.
2) Vaus return a value, just like functions, which means you need to call eval explicitly. Macros, however, return a list which is then evaluated automatically.
3) Macros (conceptually) run at compile-time, but vaus (conceptually) run at run-time, just like functions.
Vaus are a much cleaner and more consistent system, especially since it's trivial to define "fn" in terms of vau, but you can't do that with macros. But macros have the benefit that they can be run at compile-time, so in Arc they would be faster than vaus.
It's surprising that macros destroy functions rather than generate them.
Thanks for vau. It seems it's defun (and def) that are redundant. Compile-time can be modeled as run-time where t = 0, which means one could have macros at run-time. Some such macros could choose to not evaluate their results.
I don't understand what you mean by macros "destroying" functions. They are completely different concepts. They behave differently and they are used for very different purposes: they are orthogonal.
---
It is possible to design a Lisp that doesn't have a function/macro distinction. Such a Lisp would just use vau for everything. An example is the language Kernel:
So, to answer your original question: no, it is not necessary to have both mac and def, but that's how most Lisps (including Arc) do it. I think the primary reason is historical, since I think the speed argument is actually bogus for most programs.
Well, I'm not sure I'd say that... what vau does is remove the need for mac, but def is still there, to make it more convenient to define functions.
That does mean that functions can be implemented in user-land, which is very nice, but it doesn't really change functions very much.
---
Also, I prefer to not think of vau as being "the ultimate macro", I prefer to see it as a construct which is very different from macros, but happens to serve the same purpose in most cases, which is why vau can replace macros in most (but not all!) cases.
Then again, I also prefer to see functions as being separate from vaus, even if they're implemented using vau... So take what I say with a grain of salt.
It took me a while to realize that I shouldn't think of vau as a macro-like thing. Macros and functions are different kinds of evaluation policies; vau just lets you construct arbitrary evaluation policies. It's like discovering the quarks that all fundamental particles are made up of; quarks don't preferentially constitute an electron more than a neutron.
The challenge now is to build a runtime where vau can have performance comparable to existing lisp systems.
That's what vaus do... which I already covered in my second post. The word "function" in Scheme, Common Lisp, JavaScript, Python, Ruby, Lua, etc. all refer to something that always evaluates its arguments. Arc is no different.