Hacker Newsnew | comments | leaders | jobs | submitlogin
34 points by pg 861 days ago | link | parent

You can now include code in comments:

 (def codetree (file)
   (trav + 1 (readall (infile file))))
Anything that appears after two newlines and a blank space is treated as code, till there's a line that doesn't begin with a space. This is like the markdown convention, but you don't have to use four spaces; one will do.

Incidentally, the code above tells me the number of nodes in the code tree of a file. Not just leaves, which would be

 (len (flat (readall (infile file))))
but interior nodes as well. To me this is the best measure of how long a program is. I used to go by lines of code

 (def codelines (file)
   (w/infile in file 
     (summing test
       (whilet line (readline in)
         (test (aand (find nonwhite line) (isnt it #\;)))))))
but I found this was encouraging me to do the wrong things.

(This kind of test matters because I'm constantly trying to make news.yc shorter as a way of pushing functionality down into Arc.)

Here's trav, btw:

 (def trav (f base tree)
   (if (atom tree)
       (base tree)
       (f (trav f base (car tree)) (trav f base (cdr tree)))))
It traverses a tree, doing something at every node. So e.g. CL copy-tree would be

 (def copy-tree (tree) (trav cons (fn (x) x) tree))
If you're wondering how the second argument to trav in codetree could be 1, it's because a constant when called as a function simply returns itself. This turns out to be quite handy.



7 points by emmett 861 days ago | link

I like the abbreviations you're using. "w/" in particular is extremely readable. And who wouldn't love a function call (isnt it #\;) in real code. #\; looks a little bit like Perl walked into the middle of your Arc program though.

-----

2 points by gms 860 days ago | link

#\x is a character literal in Common Lisp (in this case, the semi-colon character).

-----

3 points by emmett 860 days ago | link

I'm no lisp hacker, so that part stood out to me as particularly hard to understand in comparison to the rest of the code which was perfectly clear to me. I had to re-read it a number of times to figure out what it meant; obviously, if I was at all familiar with lisp I would have had no trouble with it.

-----

5 points by gyro_robo 859 days ago | link

A truly advanced Lisp dialect will infer parentheses.

-----

3 points by mr_luc 861 days ago | link

See, THIS is the only reason I come to news.yc.

I love the idea of startups, but I've got the wanderlust. During the part of the year where I have net access, I watch ycombinator closely, and I'm not really hoping to see startup news.

-----

3 points by shiro 861 days ago | link

> constant when called as a function simply returns itself.

I thought a sequence at a procedure position acted like aref, e.g. ("abc" 1) => #\b, right? So the automatic promotion of constant to constant-function occurs only for non-aggregate types?

-----

3 points by pg 861 days ago | link

Right. (It was a revealing slip. I've already started thinking of indexable stuff as like functions.)

-----

3 points by henning 861 days ago | link

Is Arc going to support concurrency and/or manycore systems?

The original writing(s) on Arc assumed a free lunch that would continue for decades (justifying a lack of emphasis on scalability and efficiency in implementation, I thought), which already seems kind of naive. You can already buy 8-core systems from Apple, mang. It's the future.

-----

6 points by pg 861 days ago | link

It has threads and a way to say a chunk of code should be executed atomically. No more than that at the moment. Nor is there any explicit notion of a processor. I'm not sure how that will turn out.

-----

5 points by dfranke 861 days ago | link

I think that's all you need for version 1.0. All the bleeding-edge concurrency abstractions like software transactional memory are only that: abstractions. Henning is right that they're going to become increasingly important in the future, but as long as you have threads and atomicity as building blocks, you should be able to do all the rest in the macro system.

Just make sure that some concurrency package eventually becomes a de facto standard. Getting C libraries that use different threading implementations to interoperate is a nightmare, and I'd hate to see Arc go the same route.

-----

1 point by neilc 860 days ago | link

In general, combining multiple packages that each attempt to perform concurrent operations is error prone, especially if they use locking. The STM papers make an interesting argument for why using transactions rather than locks as the typical concurrency primitive leads to concurrent programs that can be more easily composed.

-----

1 point by notabel 859 days ago | link

On that note, you might take a glance at the lightweight concurrency paper; it proposes a new concurrency runtime for GHC, basically moving most of the implementation into Haskell, with just a thin layer of primitives in the RTS. Interestingly, those primitives don't include locks, but instead a very minimal transactional memory.

http://research.microsoft.com/~simonpj/papers/lw-conc/index....

(The paper is also notable for giving a complete operational semantics of the system it discusses.)

(But this stuff isn't in GHC mainline, and won't be, for now, because it needs serious performance work first.)

-----

1 point by sketerpot 860 days ago | link

That's why it's really nice to see that Arc has threads and an atomic-execution mechanism. GHC-style Software Transactional Memory would be a natural fit, and it seems to scale very well.

Locks seem to be on their way out.

-----

1 point by jsmcgd 861 days ago | link

Making a dialect that is the de facto for multi-core programming could be quite a ruse.

-----

2 points by mr_luc 861 days ago | link

Stupid questions alert. ____________________________

In On Lisp, you mentioned that implementing continuations with closures via transformation to CPS could be accomplished by writing a code walker, but that this would be a "serious undertaking" in Common Lisp.

This struck me at the time and ever since as painful, but understandable. Macros aren't first-class objects in CL, so the code you're walking through could be very complex due to macro-expansion, and I could be wrong, but I don't remember if the resulting code would necessarily contain clues that tell us what macro generated it, etc.

Could one assume (and it is definitely an assumption, or speculation, not a deduction from what you've given us here!) that it would be much simpler in Arc? Say, a page or so of code?

-----

3 points by pg 861 days ago | link

I could make it as easy as I wanted to write a codewalker, by changing the language where necessary. But it's not a priority right now. I haven't been doing anything where I felt I needed a codewalker.

-----

1 point by mr_luc 861 days ago | link

Hmmm. I guess I could also ask "What does readall do?" and go on speculating to myself, but the stuff above is what I really want to know. Once I've defined something (macro, function), what does it look like in Arc? Is its structure, either before or after macroexpansion, available to play cute little tricks with?

I'll stop asking questions now. I feel I'm reaching the limits even of assumption and might start getting silly. (For instance: "Could I write a function that let me select nodes within a loaded function using CSS Selector syntax?", but that's just silly so I won't ask it).

-----

3 points by pg 861 days ago | link

readall keeps calling read till it runs out of input, and returns a list of all the results.

-----

1 point by mr_luc 861 days ago | link

That's what I asked for, all right.

Sorry for asking obtuse questions obtusely. Let me try this way:

How much would codetree need to change to accept a function or macro as input instead of a file, and produce output that is similarly meaningful? (Not at all, a little bit, a lot).

Sorry again for poor question quality, and to harp on what increasingly appears to be an unimportant point.

-----

1 point by pg 859 days ago | link

I assume by taking a fn as input you mean taking a fn whose return values would become the input. That would not be hard, but the clean way to do it would be to modify read so that it could take a fn as an argument.

-----

1 point by plinkplonk 860 days ago | link

Very Interesting ! - especially the idea of using a count of the parse tree to optimize the language. Can you tell us anything about how well (or if) Arc is converging to a potentially releasable state?

-----

1 point by Zak 860 days ago | link

Looking at the expression

 (isnt it #\;)
I'm assuming "it" is an implicit variable meaning the result of the last expression. I'm also assuming since you have "isnt" you also have "is" and it's used like this:

 (when (and (regex-match "[0-9]" foo) (is it 4))
    (print "it is 4"))
Does boundp exist in Arc, or are unset variables null? I've always thought making them null would make code shorter, though it might cause more problems than it's worth.

-----

2 points by pg 860 days ago | link

is is just CL eq (or rather eql)

Unset variables are not null; that would lead to horrible bugs. But I don't think boundp exists either. So far I haven't needed it.

-----

1 point by Zak 860 days ago | link

Since I haven't used a language where unset variables are null or tried to design one, I'll assume you're right. I haven't had to use boundp much in Lisp, but I've had to use its equivalents in other languages quite a bit when working on other people's code for money. I suspect that boundp showing up in code a lot is a sign of badness - most likely in the choice of variable scope. I'm not sure if leaving it out will encourage people to write better code or just write their own workarounds.

Edit: aand binding test results to "it" reminds me of Apple's Hypercard. I will read the entire thread carefully before asking questions next time.

-----

1 point by aston 861 days ago | link

Automatic creation of the numerical lambda return. Clever...

Why "atom" instead of "leaf"? Especially since you acknowledge it's a "tree"?

-----

2 points by pg 860 days ago | link

It's even simpler than that. I changed function application so that if the first element of the expression is a simple type (e.g. a number), it just gets returned.

-----

1 point by aston 859 days ago | link

That's cool enough. Though, potential spot for allowing subtle bugs? If I'm tossing around what I believe is a function (but instead happens to be a simple type, maybe even like #f or something), when I apply it I'd prefer to get a runtime error instead of a silent value return.

-----

3 points by pg 859 days ago | link

Having a more dense language inevitably means that more programs turn out to be accidentally meaningful. If the power of a programming language is the inverse of how long programs are (which is the best definition I've found so far), you can't make a language more powerful without the space of meaningful programs becoming denser.

-----

3 points by pg 859 days ago | link

Having a more dense language inevitably means that more programs turn out to be accidentally meaningful. Since Arc is designed to be an LFSP, that trade-off is worth it.

-----

1 point by cwarren 860 days ago | link

Figure I might as well post this here as well:

Numbers _are not_ functions! This kind of attitude is what gets you python's list formatting operator:

>>> "%s" % ("a string",)

'a string'

>>> "%s" % ["a string"]

"['a string']"

I've been burned by that before, and I'm not exactly stupid.

(I don't think this posted the first time. Forgive me if this turns out to be a double.)

edit: Bah, I can't get this code to format properly. How's that for ironic ;)

-----

1 point by Zak 860 days ago | link

Arc seems to take a very implicit approach to things - rather unlike Python. I haven't written anything non-trivial in Python, but it looks like the problem with the list formatting operator is that it's an infix operator that depends on similar looking characters (paren and square bracket) for its syntax. I think it might be less confusing as a function or method:

 list_format("%s", ("a string",)
 list_format("%s", ("a string",)
 ("a string",).format("%s")
 ["a string"].format("%s")
Looking at it on the screen, making it a method call looks far less confusing. Arc treating constants and sequences as functions doesn't seem like the same kind of thinking to me.

-----

2 points by earthboundkid 860 days ago | link

For the record, in Python 3000, they're changing the printing and formatting around a lot, so the example above will become:

 "%s".format("a string")    #yields: a string
 "%s".format(["a string"])  #yields: ["a string"]
 "%s".format(("a string",)) #yields: ("a string",)
This will eliminate the confusing difference in the treatment of of tuples and lists when formatting a string. I think it's more clear, although I imagine some people will complain that "format" takes longer to type out than "%".

-----

1 point by cwarren 860 days ago | link

Perhaps I should've elaborated a little. Here's a follow-up that I posted on reddit:

both of them violate the user model in subtle ways[1]. Most people don't expect numbers to act as functions. If a bug crops up because of it, more than likely they won't check to see if that's the problem. Again, I'm not against brevity. Just don't make functionality implicit in situations when the programmer isn't expecting it. If you use it so much, use a symbol prefix like `--I don't care. Just make it explicit.

In python's case, it's because it treats tuples and lists differently. Tuples and lists are almost always identical in python. The user's assumption is that they will also be identical in this case, when in fact they aren't.

[1] User interface design is surprisingly helpful when designing programming languages. It's fairly obvious why, but most people don't realize it.

-----

1 point by nostrademons 859 days ago | link

Arc is supposed to be a LFSP. Smart people adjust their user model to the tools they have available (right?), so it's at least consistent with Arc's design principles.

It's not the choice I would've made - I tend to agree with you that "explicit is better than implicit". But languages all have to make certain assumptions about who their users are (same with UIs, really), and this design decision is consistent with Arc's previously-stated design philosophy.

-----

1 point by shiro 860 days ago | link

As far as you're thinking in procedural mind, probably there seems to be a big difference between constants and procedures (functions).

Once you are converted to functional mind, difference between a constant and a function that returns a constant is very subtle. When you use combinators a lot, you no longer think functions as something "invoked" or "called" in the similar sense as in procedural languages.

A possible pitfall in this case is that Arc is dynamically typed language. I usually program in Scheme, but when I'm passing function-returning-function-returning-...-functions around a lot, sometimes the 'one-function-level-off' error becomes hard to track down. Implicitly promoting a numeric constant into a constant function possibly delays catching this bug (since it masks the function level difference) but I doubt that it makes situation much worse. I think optional type declarations and type inference would be a lot of help.

-----

2 points by nostrademons 859 days ago | link

It's not really a procedural vs. functional distinction - I'm fluent in Haskell and had the same initial reaction as cwarren. Rather, Arc is "weakly typed". The same bit of program data can be interpreted as different types depending upon the context where it's used. (This is distinct from strong-but-dynamic-typing like in Scheme or Python, where you have to explicitly convert between types.) It joins the club of Perl, PHP, and assembly in this regard.

I mentioned elsewhere on this thread that I think this is the right design decision given Arc's design principles, but that those design principles are flawed. In my experience, bugs resulting from implicit coercions are rare, but they're also really difficult to track down. That was a major reason I switched from PHP to Python.

-----

3 points by shiro 858 days ago | link

OK, I stand corrected. Statically-typed minds also frown on this. It might be only Lispers that feel differently (after all, they've been conflating an empty list, a boolean false, and a symbol NIL and insisting it's the right thing).

-----

1 point by Zak 860 days ago | link

>User interface design is surprisingly helpful when designing programming languages.

I don't know why this is surprising (though I don't dispute that most people find it so). I think both Python and Arc pay a lot of attention to this principle, though their philosophies on the subject are quite different. I agree that allowing constants to be called as functions could be a source of bugs. It also seems like it could be, as PG says "quite handy". I'll have to see for myself when Arc is released.

-----

1 point by minimalcriminal 861 days ago | link

I have some questions about arc if you don't mind :)

Why 'whilet instead of 'while and why 'aand instead of 'and? I thought one of your aims was to produces a minimal set of "axiomatic" operators (functions, macros, special forms) which gave the maximum utility. So couldn't these be generalized into a single operator?

Also, isn't ")))))))" overkill for such a simple function, Can reader macros be defined within lisp? (like if you wanted to make } close all parenthesis up to the top level for example)

-----

4 points by pg 861 days ago | link

They're different from while and and: whilet is a combination of while and let that binds its first argument to the result of the test. aand binds the result of successive tests to "it" for use in succeeding tests.

))))))) isn't overkill to Lisp hackers. Lisp hackers read code by indentation and rarely notice the parens, especially terminating ones. There have been dialects (Franz Lisp) that used ] to close off all open parens, but it would be a waste to use up a good char like ] to fix a non-problem.

-----

1 point by ricky_clarkson 860 days ago | link

I expect that ] to close all open parens would lead to code that is even tighter jammed to the left margin. I like that I feel comfortable writing nested code in Lisp, and if I was always looking for somewhere to put a ] I'd be tempted not to write nested code, and hence start leaking private identifiers.

-----

1 point by minimalcriminal 861 days ago | link

Ah that makes sense! Hope you don't mind but is there any ideas for a loop/iterate/series type macro in the works?

-----

3 points by pg 860 days ago | link

Arc has a bunch of iteration constructs. It's iteration-friendly. Most previous Lisps have been ambivalent about iteration, because their designers didn't like side effects.

The usual Lisp do macro, for example. What a wretched bit of language design. Even now, whenever I encounter one, I have to stop and translate it in my head. Plus it can be very verbose. The reason do is so bad is that whoever designed it wanted to make it as functional (in the no-side effect sense) as possible. But sometimes side effects are just the right model.

BTW, there is a do in Arc. It's the new name for what used to be called progn. (It's surprising how much better that little change makes code look.)

-----

1 point by sketerpot 860 days ago | link

Does Arc have an API for iterating across general types of data structures? The classic example is lists and arrays; in Common Lisp, supporting both can be a hassle and the language itself seems ad hoc when the issue arises. MAP supports both, but MAPCAR doesn't; the LOOP macro has seperate "IN" and "ACROSS" syntax, and no (standard) way to extend this to user-defined data types.

-----

4 points by pg 860 days ago | link

This sort of thing works much more cleanly in Arc.

-----




Lists | RSS | Bookmarklet | Guidelines | FAQ | News News | Feature Requests | Y Combinator | Apply | Library

Analytics by Mixpanel