At first I thought you were saying ":d" was the comment, which struck me as a clever way to make code with confusing positional args more readable without complicating the semantics. I thought maybe you were moving away from keyword args. ;)
But really it's ":" by itself that's a comment. If the reader would read a ":" symbol, instead it ignores that symbol, and there are some idiosyncratic interactions with semantic whitespace. We could duplicate this technique in Racket by sprinkling "#;:" around our code, but ":" is less clutter.
If the only point is to have intentional clutter but not a lot of clutter... then I don't get it. Does the interaction with semantic whitespace actually play a crucial role here?
No, I think this idea works even in a traditional lisp without indent-sensitivity. The problem is that lisps have an if or cond form that interleaves expressions with very different semantics. Lots of other forms have multiple kinds of expressions, but usually the first expression is of one type (bindings) from the rest, or something like that. cond is special because it continues to alternate semantics all the way down, and as the individual expressions grow complex it gets hairy to read. I think judicious use of colons in this manner make if/cond more readable. Especially to non-lispers.
I deliberately chose the example in the commit message to be fully-parenthesized. It's taken from news.arc, and I was trying to show that the colons help distinguish tests and branches. Was that not helpful?
Ah, I didn't realize you disabled indent-sensitivity inside of parens, so I thought that example's indentation might have been important.
You could be onto something with ":". What do you think of these styles? ^_^
(Sorry for the huge examples.)
(if : !user
(submit-login-warning url title showtext text)
: (~and (or blank.url valid-url.url) ~blank.title)
(submit-page user url title showtext text retry*)
: (len> title title-limit*)
(submit-page user url title showtext text toolong*)
: (and blank.url blank.text)
(let dummy 34
(submit-page user url title showtext text bothblank*))
: (let site sitename.url
(or big-spamsites*.site recent-spam.site))
(msgpage user spammage*)
: (oversubmitting user ip 'story url)
(msgpage user toofast*)
(let s (create-story url process-title.title text user ip)
(story-ban-test user s ip url)
(when ignored.user (kill s 'ignored))
(submit-item user s)
(maybe-ban-ip s)
"newest"))
(if !user
(submit-login-warning url title showtext text)
: (~and (or blank.url valid-url.url) ~blank.title)
(submit-page user url title showtext text retry*)
: (len> title title-limit*)
(submit-page user url title showtext text toolong*)
: (and blank.url blank.text)
(let dummy 34
(submit-page user url title showtext text bothblank*))
: (let site sitename.url
(or big-spamsites*.site recent-spam.site))
(msgpage user spammage*)
: (oversubmitting user ip 'story url)
(msgpage user toofast*)
(let s (create-story url process-title.title text user ip)
(story-ban-test user s ip url)
(when ignored.user (kill s 'ignored))
(submit-item user s)
(maybe-ban-ip s)
"newest"))
When I was working on Penknife, I designed every "if" macro to insert parentheses for its else clause. Mainly I did this so I could put different kinds of conditionals like 'if and 'iflet together in what looked like a single branch, but I liked its effect on self-documentation, which is similar to the use of your ":":
(if !user
(submit-login-warning url title showtext text)
if (~and (or blank.url valid-url.url) ~blank.title)
(submit-page user url title showtext text retry*)
if (len> title title-limit*)
(submit-page user url title showtext text toolong*)
if (and blank.url blank.text)
(let dummy 34
(submit-page user url title showtext text bothblank*))
if (let site sitename.url
(or big-spamsites*.site recent-spam.site))
(msgpage user spammage*)
if (oversubmitting user ip 'story url)
(msgpage user toofast*)
let s (create-story url process-title.title text user ip)
(story-ban-test user s ip url)
(when ignored.user (kill s 'ignored))
(submit-item user s)
(maybe-ban-ip s)
"newest")
Nowadays I like the idea of implementing Arc's (a:b ...) ==> (a (b ...)) syntax in the reader, and that would make my Penknife design pattern unnecessary:
(if !user
(submit-login-warning url title showtext text)
:if (~and (or blank.url valid-url.url) ~blank.title)
(submit-page user url title showtext text retry*)
:if (len> title title-limit*)
(submit-page user url title showtext text toolong*)
:if (and blank.url blank.text)
(let dummy 34
(submit-page user url title showtext text bothblank*))
:if (let site sitename.url
(or big-spamsites*.site recent-spam.site))
(msgpage user spammage*)
:if (oversubmitting user ip 'story url)
(msgpage user toofast*)
:let s (create-story url process-title.title text user ip)
(story-ban-test user s ip url)
(when ignored.user (kill s 'ignored))
(submit-item user s)
(maybe-ban-ip s)
"newest")
This particular style really would conflict with your keyword arg syntax, and I don't expect it to work in unparenthesized expressions, but maybe you can get something out of it anyway.
Thanks for those examples! Yes, I didn't want to hardcode ':' for just this one cond use case I had in mind. I wanted to let people highlight the tests rather than the branches if they preferred.
One nit: I wish the first two gave branches more indent than tests; I think I'm comparing them to switch statements.
In this fragment from example 1:
(let dummy 34
(submit-page user url title showtext text bothblank*))
: (let site sitename.url
(or big-spamsites*.site recent-spam.site))
..and in this one from example 2:
(let dummy 34
(submit-page user url title showtext text bothblank*))
: (let site sitename.url
(or big-spamsites*.site recent-spam.site))
..the colon isn't separating the cases clearly enough IMO. There's a confounding vertical alignment that distracts the eye.
The discussion on readable-discuss suggested example 2, but it would require editor support to ensure indenting a line doesn't move the ':' at the start. Autoindent settings would also interfere. But they're like #ifdef's, so there's some precedent for doing things this way. Keeping the ':' in column 1 would eliminate the need for clarifying that the colon "does affect the indentation of a line.." That might make them more intuitive.
I hadn't considered the latter two ideas; they are very compelling indeed. I'm going to think about them more.
Ah, I didn't realize you disabled indent-sensitivity inside of parens, so I thought that example's indentation might have been important."
Actually even that is irrelevant ^_^. Even before I disabled indent inside parens those examples wouldn't insert parens anywhere because the inner clauses are all fully parenthesized. Wart has never wrapped a line in parens if it already began with parens.
Basically paren insertion is extremely conservative and avoids messing with code as much as possible. Now that I've disabled it inside parens this is even more true.
"This is a common complaint of mine. Why do people do this? Why are they so concerned with getting it right before they're willing to let the world see it? If you throw it out earlier, who knows, somebody might come and contribute earlier. I'm more motivated to contribute when something is half-baked. Once you've figured it all out, it hardly seems worthwhile :) "
This part of what you're saying could well be reasoning in favor of discussion before code. Coding is a process of developing ideas, but so is discussion. This person has let the world see their ideas in the form of a presentation, rather than obsessing over getting them "right" in the form of runnable code first.
Unless... you're not even asking for runnable code? Interesting. Are you asking for people to be comfortable enough to do public brain dumps of all their works-in-progress, regardless of how useful they expect them to be?
No I want runnable code. But isn't all code somewhat runnable? Otherwise it wouldn't be code. Almost any project is runnable within a few hours.
Your argument assumes that the presentation is less work than code, but I don't think that's true. He's clearly put hours of effort into presentation, but there isn't enough for me to even be clear on what he's proposing. Code would be unambiguously concrete in this respect. Even if it only works some of the time, if it has bugs, etc. I'd be able to get a sense of how it ought to work.
"Almost any project is runnable within a few hours."
"Your argument assumes that the presentation is less work than code, but I don't think that's true."
I take this a little personally, because there are many projects where I still don't even know what I want several years in. :) Well, programming is all about knowing what one wants, but I mean I don't even know these projects well enough to identify the core program I should start with. But I like to think I thrive on these ideas, because interesting big projects are the main reason I even give a second thought to little one-day projects.
Also, programming has a skill aspect to it. Unless someone's used a certain tool or technique before, it can be frustrating and intimidating. I personally find several things frustrating that others take for granted, like Emacs, Vim, manual memory management, the command line, and yes, riding a bicycle. :-p If someone's not ready to code up even a hackish language yet, I can relate.
I certainly didn't mean to make it personal. I didn't even think I was talking about you.
I vaguely sense that we're using very different meanings for words like "runnable", "less work", "program", "project" and "right". But now I'm afraid to pick at this further.
I'm not claiming there should be no discussion without code, or that people must have working code when making a proposal, or anything nearly that strong. In this case from the certainty and polish of the presentation I assumed he knows what he wants. And he's referred to code so we know it's not a pure spec. So the bottleneck seems to lie in me understanding his proposal. And I was suggesting that sharing whatever code he has might help me over that hump. Showing code can only ever help, never hurt.
"I certainly didn't mean to make it personal. I didn't even think I was talking about you. [...] now I'm afraid to pick at this further."
Oh, sorry. I'm personally invested in this topic, but I'm not offended. But come to think of it, my post was a few claims fluffed up with personal foibles in place of other justification, and thanks for not being eager to refute the acceptableness of my foibles. :-p
---
"In this case..."
I don't have much of an opinion in this particular case. I was spurred on by the "common complaint" that people don't share their code in progress, and I'm interested in what kind of overall strategy we should pursue in response.
- Social networks for code sharing (e.g. package managers, HTTP, GitHub)?
- Collaborative development of large-scale online worlds (e.g. Wikipedia)?
- Socially encouraging or discouraging people to program depending on their personality?
- Investigating what kinds of programming problems are so mathematically exotic that meaningful code is exactly the thing that's hardest to develop?
- Different laws and licenses related to sharing code?
---
"And he's referred to code so we know it's not a pure spec."
I don't remember that part. I did skip a few boring parts in the video. ^_^;;
Hmm, maybe I even consider runnable code to be relatively boring and forgettable. XD; Probably depends on whether it's a product I'm eager to use right away. ^_^
"The grouping symbols are much harder to parse than parentheses. Parens are bigger than characters, and our brains can easily pick them out. A symbol can't be so easily found, especially when it is mixed in with other symbols."
When I use grouping symbols, I choose symbols like "-" and ":" which skirt that issue. I've considered setting a language-wide policy to use these symbols exclusively for grouping, but I haven't yet designed a language where that policy would fit in. (It would fit into Arc or Scheme, for instance, but I didn't design those. :-p )
---
"you could only make the first body argument to let the first odd item in the arguments to let that has parens, and not simply the first item in the arguments that has parens, but that prevents this from being working code"
The technique I use in Lathe's 'xloop is to greedily consume variable-and-anything pairs from the body. This terminates either when there's a non-variable at an odd location, or when there aren't enough elements left to make a pair, whichever comes first.
On a more extreme note, I'd kinda like to see most binding happen in 'do. If a variable-and-anything pair appears in a (do ...) form, that binding should hold for the remainder of the form. I implement this in Lathe as 'lets.
---
"Implicit parens? That's insane!"
I don't get it either. :) But if the idea is to have a "there are only a few ways to write it"[1] approach to syntax, that would explain both the implicit paren enforcement and the mandatory four-space indent.
By the way, I think you might need to write your 'let example like this:
let top (+ (- b
- (expt b 2
* 4 a c)
,bottom (* 2 a
/ top bottom
I've never used Nu before, but it's one of the first things I'd consider if I had to build a program on an Objective-C platform.
Let's see....
Here's an interesting overview of some of the syntactic choices and quirky pseudo-conveniences in Nu: http://programming.nu/operators
Nu seems to have a certain selection of symbol-level syntax. (Suffering succotash.) A symbol beginning with * is a rest arg, a symbol beginning with __ becomes a gensym during macroexpansion, and a symbol beginning with @ refers to a class attribute. Objective-C message call syntax like (a sendMessageWithArg:b) seems to be used, and I bet that's part of the reader.
Nu has a seemingly monolithic class definition syntax, which probably compiles straightforwardly to Objective-C classes, but I haven't found documentation about it.
Yes indeedy. And as already mentioned in that thread, I liked the name enough to keep it. I figured that since Arc/Nu is a compiler for the Arc language, rather than a full blown Objective-C/whatever Lisp, the confusion should be fairly minimal...
Incidentally, my new language that I've been steadily working on[1] is called "Nulan" which is short for "Nu language" :P
---
* [1]: It's worked for a while now but I still need to get the parser into a good state... turns out, syntax is hard!
I began to write this post as a companion to my posts about dataflow[1], since neither a graph syntax nor a hierarchical syntax for dataflow does much to address module boundaries. It took me a while to get my thoughts into a single focused topic, and this is the result.
By limiting module exports to only query-answer pairs for which no other answer could ever be provided, we can cause the effect of modules on a system's overall knowledge to be declarative and monotonic. No program in this system ever changes meaning, but some programs' meaning is unknown without access to a sufficient knowledge base.
If we relax that export constraint to allow exports for which only one author can change the query answer and they have little incentive to cause harm that way, we can preserve most of these properties but gain extra bonuses: The language implementer can use a much less mathematically sophisticated query resolver, and the language user can capture informal, change-tolerant program dependencies with informal queries.
If a where clause's binding expression ran after the body in an eager language, we couldn't use the value during the body. We'd be limited to using it from inside closures (manual laziness).
I do have ideas, but none that make sense. :-p It does seem better to embrace top-to-bottom side effect ordering throughout all syntaxes of the language, with as few discontinuity points as possible.
But if we want to embrace that, it's probably hypocritical if we use prefix syntax for procedure application. ^_-
Working closely off of two papers[2][3], I've implemented an incremental garbage collector in JavaScript, paying close attention to a particular reachability interaction between weak tables and immutable structures. I'm not sure anyone has addressed this interaction before.
I don't really have much use for Cairntaker yet, but it jives with my personal intuitions about reachability, and I expect it to come in handy for my own future language implementations.
[2] "Ephemerons: A New Finalization Mechanism," Hayes '97.
[3] "Real-Time Non-Copying Garbage Collection," Wilson and Johnstone '93.
Running Rainbow or Jarc from a jar is easy, but there are some caveats:
- Both Rainbow and Jarc expose JVM reflection capabilities to Arc, and thus provide the Arc program with ambient authority. This is convenient for an application written in Arc, but it means we should only run Arc code the user trusts. (Rainbow might be possible to tame by nilling out some global variables, but I wouldn't count on it, and it doesn't seem in the Arc spirit.)
- Both Rainbow and Jarc intern symbols into a static field, so if someone's Arc coding style causes their program to intern lots of symbols, it may be difficult to reclaim the memory. (In fact, I wonder if Racket has the same issue.)
- Rainbow stores global variable values in the symbols themselves. Since these symbols are interned, it would be difficult to manage more than one global namespace during any given run of the JVM program, let alone to manage more than one independent Arc runtime, as a REPL program might like to do.
- Jarc has support for bytecode generation, but it probably won't work on Android. I don't think Jarc depends on bytecode generation for normal functioning, but I don't know Jarc well enough to be sure.
- Rainbow's example code, including its welder.arc text editor, makes heavy use of Swing, and I doubt that will work on Android.
Interned and unreadable symbols are only weakly held by
the internal symbol table. This weakness can never
affect the result of an eq?, eqv?, or equal? test, but a
symbol may disappear when placed into a weak box (see
Weak Boxes) used as the key in a weak hash table (see
Hash Tables), or used as an ephemeron key (see Ephemerons).
Kinda. It irks me that a value that's still arguably reachable (via 'read) could be observably lost by a weak box, weak hash table, or ephemeron. But it seems feasible to create readable-counts-as-reachable versions of these data structures on top of the built-in ones, so the built-in ones expose strictly better lower-level control.
Racket's new submodule design is geared so that a piece of Racket code can be structured so that people can not only import it as a library, but also run its tests, run it as an application, or use it as a language. Essentially, this gives you multiple verbs you can use when you see a Racket file laying around. :-p
Where before a Racket module was essentially some imports, definitions, side effects, and exports, now it will also have child modules, which can be accessed without executing the parent. Certain child module names like "main" and "test" are special-cased by language.
Submodules can be imported without importing their parent:
(require (submod "cake.rkt" extras))
---
Editorial
I don't think this has significant consequences for Arc, except as a potential tool in maintaining the Racket versions of Arc.
If someone finds this an inspiring example of language design, that's their prerogative. :-p I do like its simplicity and practicality, but I'd hesitate to say it's a very seamless design, thanks to hardcoded names like "main". It has a convention-over-configuration flavor to it in that respect.
I wonder if Racket is dynamic enough to have supported this kind of thing as a sublanguage, but I doubt it. Racket kinda has first-class namespaces, but modules--and names imported from modules--are difficult or impossible to manipulate programmatically, and this doesn't do anything to ameliorate that.
So, how'd it go? Did someone end up presenting Arc? I've been considering presenting Arc to the Lisp group in NYC, if I can put together a five-minute presentation for Lispers who don't know much about Arc.
It was very fun, but no talk on arc. I ended up talking about http://arclanguage.org/item?id=16378. I'd love to see a presentation on arc; I wasn't sure five minutes was nearly enough.
That sounds like a good talk. Anything beyond your post? I must admit that I haven't put enough time into the macro-apply situation, so I'm not even up to speed with your post, butI'd love to see slides if you have them.
My reason for giving a five-minute talk about Arc is -- well, beyond the usual "I don't know if I can do a longer one" -- I've talked to a few Lispers about Arc, and they're not very excited about it. In fact, it's denigrated. I wanted to give a short talk to convince people they want to learn more. Maybe not 5 minutes, but 15? I don't know.
Yeah I have other, less politically-correct, reasons not to want to talk to lispers about arc :) The reasons for not wanting to talk are themselves hard to talk about.
I'm not interested in trying to convince lispers about anything because they are pretty close-minded about their tools. Trying to evangelize anything risks a minor flamewar, and even if you avoid flamewars you end up thinking you might have better spent your time talking about something else.
At yesterday's talk there was the obligatory old dude speculating that I might be able to accomplish my already-eval shenanigans using reader macros on backquote. In itself there's nothing wrong with the idea. It's totally false[1], but that's ok. The bigger criticism is that I could predict ahead of time that I'd get some question like that. There's a huge bias in the sorts of questions that get asked by CLers, and that predictability is utterly boring. I find questions about why I don't use Apple similarly boring.
Crotchety old lispers are fun to talk to. Often I learn some gem I couldn't find anywhere else. At the risk of sounding condescending, I'm aware I'm on my way to being a crochety old man myself[2]. I've just learned to avoid certain topics -- and to change the subject when others don't know to avoid them -- so we can all get along.
Talking about lisps is like talking about text-editors. Perhaps it's more important to focus on what we do with our tools. A cool app in arc will be more effective at getting lispers to engage with arc than any words we can come up with. But you'll still have to fend off questions about why you couldn't just use Common Lisp for the purpose. Snort.
That said, I still would love to watch you try to explain arc to non-arc folks. Sometimes explaining a thing helps understand it better. The audience will almost all take it constructively. You'll just need to put up with the prospect of the occasional miscreant. And if you do you'll be a better man than me.
[1] I don't even need to know anything about reader macros to make that assertion, because my feature isn't just some syntactic sugar. It requires semantic support for fexprs. CL has no fexprs. QED.
[2] My girlfriend says I must have been 60 when I was born.
---
I don't want to sound like it was a terrible time. I had a fantastic time there, lots of great conversation with a lot of interesting people, and will definitely go back the next time. But even at a lisp meetup the best conversation is only tangentially about lisp. There was one lightning talk about building a distributed RDBMS inspired by Connection Machine principles. The guy didn't build it in lisp, but he figured lispers might be worth showing it to. And that is awesome; I think many people got a lot out of it.