import math,itertools as i
def f(k,p):
while(len(p)>k):
m=()
for r in i.combinations(p,2):
d=math.sqrt(sum(map(lambda x,y:(x-y)**2,*r)))
if d<m:m=d;x,y=r
t=p.remove;t(x);t(y);p+=[map(lambda g,h:(g+h)/2,x,y)]
print p
I'm resubmitting http://arclanguage.org/item?id=16697 because we didn't do it justice the first time around. I could swear I read it, and yet apparently I didn't notice many things:
b) The idea of overloading * analogously to +. And of making avg variadic. zck, do you want to update anarki? I'm happy to do it otherwise.
c) Quotable quote: "I thought of no simple way to do this but functionally, using recursion. The second place winner, who wrote in Python, approached this imperatively, using mutable state. We actually had to explain our solutions to each other, as the minified versions were not easy to gather the algorithm from. Programming styles can be restrictive."
All this was worthy of comment the first time it was submitted. Thanks, zck.
I don't know if you saw the thread about infix in wart[1], but I can say:
def (y x)
(m*x + b)
All operators have the same precedence, but operators without whitespace have precedence over operators with whitespace:
(n * n-1)
The major drawback is, of course, that you can no longer use '* or '- in symbols. I use capitalization for globals and underscores in long names, and have been mulling camelCase.
(Wait, didn't someone check the pitchforks at the door?)
Yes, the whole "this" system in JS is pretty insane. Naturally Nulan solves this by requiring you to explicitly pass in the object. This also gives you the "call" and "apply" behavior of JS for free, in an orthogonal way.
P.S. The JS version of Nulan is coming along quite nicely. 25 working macros and 10 semi-working macros. It can handle a ton of stuff already, but it still has a long way to go!
Interesting... doing that test in Arc/Nu returns "t" in both cases. I wasn't even aware of that issue but apparently "fixed" it accidentally.
By comparing Arc to Arc/Nu, it seems that the problem is that Arc returns a new list that is 'nil terminated, whereas Arc/Nu uses () for 'nil, so it doesn't have to do any conversion.
To discuss your specific issues, I prefer '+ to 'join for lists. I don't particularly have an opinion on how it looks in infix, because I don't like infix. Certainly
('(1 2 3) + '(4 5))
does look pretty ugly, but I like infix for basically everything. Your heuristic seems quite reasonable, though. What do you do about math? Do you still hold to it? For example, how would you write y(x) = mx+b:
(def y (x)
(+ (* m x)
b))
(def y (x)
(+ (m * x)
b)
(def y (x)
(m * x
+ b))
Or something else? I prefer the first one, but I know not everyone does.
On a slightly different topic, I find '+ vs 'join really weird. They are only superficially similar; '+ actually reaches inside its arguments to muck with the cons cells:
(originally written up by me at http://zck.me/golf). 'mappend uses '+, so it's also weird. This behavior seems insane to me, and was the cause of some hard-to-track-down bugs in the code at the above link.
I don't know exactly what you're asking for, but I'll try to help you. :)
---
Thank you, Pauan.
Ok. I made a mistake not to call apply.
Now it is clear. I learned from this mistake.
---
The phrase "apply it" is confusing because we sometimes say "apply a function (to some arguments)" even when we're not calling apply.
As a verb, "to mistake something (for something else)" means "to misunderstand something (thinking it's something else instead)." As a noun, "a mistake" is an accidental occurrence. The idiom "to make a mistake" means to cause an occurrence by accident.
The word "mistake" isn't usually broken up into "mis-" ("incorrectly") and "take" ("understand").
Well, this behavior is the one that surprised me initially:
o.foo() -> Object
I expected the property access and the procedure call to be two completely separate steps. Instead, there's a secret avenue that somehow exposes my local variable "o" to the callee without me explicitly passing it in!
After this encounter, I reasoned that a.b( ... ) must be its own syntax, only superficially related to a.b and b( ... ). My choice to use this syntax is explicit. This intuition worked well for a while, but then this behavior surprised me:
(o.foo)() -> Object
((o.foo))() -> Object
So it's its own syntax, but it also supports grouping parentheses? What for?
Turns out the spec actually specifies this behavior in terms of "References." An expression doesn't just return a value; it returns a Reference that can be either resolved to a value, assigned to, deleted, passed to typeof, or called as a method call. (Maybe there are some other options too.)
In the spec's own words:
"The Reference type is used to explain the behaviour of such operators as delete, typeof, and the assignment operators. For example, the left-hand operand of an assignment is expected to produce a reference. The behaviour of assignment could, instead, be explained entirely in terms of a case analysis on the syntactic form of the left-hand operand of an assignment operator, but for one difficulty: function calls are permitted to return references. This possibility is admitted purely for the sake of host objects. No built-in ECMAScript function defined by this specification returns a reference and there is no provision for a user-defined function to return a reference. (Another reason not to use a syntactic case analysis is that it would be lengthy and awkward, affecting many parts of the specification.)"
An ECMAScript Reference is similar to what I independently called a "fork" in Penknife. Forks were my approach to unifying Arc-style metafns, setforms, and macros into a single syntactic mechanism. A Penknife syntactic abstraction would return a fork value, and that value would have behaviors for parsing a macro call body, for setting, for getting, and for acting as a top-level command. The result of the parsing behavior would be another fork, so this branching process could handle multiple layers of brackets, metafn style. (Now I understand this as a monadic, coinductive approach. Yay big words.)
I've compared JavaScript's method invocation to metafns before: http://arclanguage.org/item?id=12094. I was somewhat supportive of the combination in that post, but I think Arc, JavaScript, and Penknife just use this technology for name punning, rather than resolving any essential conundrums of language design, so I'm not very enthusiastic about the technique in general.
P.S.: I've also casually explained this part of JavaScript's method invocation to you before: http://arclanguage.org/item?id=14665 But it was technically a different example, so no worries. ;)
I've been experimenting with two kinds of highlighting for comments: http://i.imgur.com/H1h7M.png (http://github.com/akkartik/wart/commit/b922700733#diff-17). The 'landmark' comments are in the lighter blue (cyan). They're often section headings, but not always. Notice how I highlight the parts of the repl, and connect up the parts of the read stage with later phases. This might help highlight the one key line in a large main function that leads to the true skeleton of the program.
Still, I'm not sure this is a good idea. Perhaps it's not that important, and the gains don't compensate for constantly thinking about how many comment leaders to type in each line. Perhaps it's something we shouldn't think about in the first draft of a new feature.
"Why does it pass { bar: g1 } as the first argument to nulan.macro_?"
That object becomes the "captures" property in env.foo.val.captures.bar later. In Penknife, that's how I let code have run time access to the lexical scopes of compile-time-expanded macros.
---
"Why do you do (0, g1.val)? That's the same as just using g1.val."
I like to go out of my way not to pass a "this" parameter unless I actually want to pass it. If I were hand-writing this code, I would have probably used a fresh local variable to hold g1.val, but this this (0, g1.val)( ... ) approach is sufficient for compiler output.
"Just use my Function( "x", ... )( x ) pattern. Keep in mind that you should only need to translate to JavaScript once per eval or top-level command, rather than once per macro call. The "AST" {bar qux corge} will still exist as an intermediate representation, since we still need to process 'bar if it's a macro and compile 'qux and 'corge if it isn't."
Ewwww. Slow. And yes I know you only need to eval once per top-level form, but the problem is that means you need to actually keep the top level forms around and the compiler/runtime needs to always be present.
Keep in mind that I want to use Nulan to write Chrome Extensions. Now imagine a popup that occurs when the user presses a button. This popup is coded in Nulan. Now, every time the user presses the button, it has to re-parse, re-compile, and re-eval all of the Nulan code that is inside the popup.
I consider that unacceptable for my desires, where the code should be about as fast as handwritten JS. So, Nulan compiles things ahead of time to a JS file, to achieve those speeds.
---
"Well, I did it for global scope anyway. You say you're doing this for local scope too?! The boxes for a function's parameters don't even exist at compile time, right?"
Well, in Racket, I just use a "let" for local scope, so no, locals aren't boxes. But I could make locals use boxes too. It would just be a lot harder to implement. At that point I might as well just write an interpreter.
And in fact, the interpreted vau version of Nulan does use boxes for all variables, global and local.
---
"Sure it does! I usually use { val: _ } when I want a box."
Yes, but then it has to do the boxing/unboxing at runtime in addition to the variable lookup. Whereas with Nulan's scheme, you get almost all the same benefits, but with just the variable lookup. The only downside is that the boxes are no longer available at runtime (but you can access the fake "boxes" at compile-time).
---
"Just wait until someone comes along and thinks your language is too static. ;)
That approach is also good for debugging. Compiling to so-called idiomatic JS is a sacrifice many JS-targeting languages make right now."
Yes it is a tradeoff. Yes I know it's more static than Arc or the Racket version of Nulan. But with my scheme, I can actually make things feel fairly fluid. As an example, this works:
(var + (fn ...))
(+ 1 2)
That is, the runtime function + shadows the macro +, so it doesn't do the macro expansion. And macros can hygienically expand to runtime values, as I already explained, thanks to the variable renaming scheme.
So, the only real downside is that you can't do this:
(def bar -> ...)
(mac foo ->
(bar ...))
That is, you can't actually call a runtime function from inside the macro. But you can still expand to it:
(mac foo ->
{bar ...})
And you can also evaluate the "bar" function at compile-time, which would make it available:
(&eval (def bar -> ...))
(mac foo ->
(bar ...))
I consider this to be a perfectly reasonable downside in exchange for increased speed.
"That works just fantastic in Racket, where you can splice arbitrary things into the AST, but that won't work in JS, where things generally have to be given a name."
Just use my Function( "x", ... )( x ) pattern. Keep in mind that you should only need to translate to JavaScript once per eval or top-level command, rather than once per macro call. The "AST" {bar qux corge} will still exist as an intermediate representation, since we still need to process 'bar if it's a macro and compile 'qux and 'corge if it isn't.
---
"If the symbol refers to an immutable box, the compiler will unbox it and splice it in directly. If it's a mutable box, it'll put the box into the AST, which will be unboxed at runtime."
I did this in Penknife too, but Penknife only had mutable boxes.
Well, I did it for global scope anyway. You say you're doing this for local scope too?! The boxes for a function's parameters don't even exist at compile time, right?
---
"JS doesn't have boxes"
Sure it does! I usually use { val: _ } when I want a box.
You should probably keep using separate JavaScript variables, but here are your examples using boxes:
// (var foo 1)
(function () {
env.foo = { val: 1 };
})();
// (def bar -> foo + 5)
(function () {
var g1 = env.foo;
env.bar = { val: function () {
return nulan.plus_( g1.val, 5 );
} };
})();
// (var foo 3)
(function () {
env.foo = { val: 3 };
})();
// (bar)
(function () {
return bar();
})();
// (mac foo -> {bar qux corge})
(function () {
var g1 = env.bar;
var g2 = env.qux;
var g3 = env.corge;
env.foo = { val: nulan.macro_( { bar: g1 }, function () {
return [ g1.val, g2.val, g3.val ];
} ) };
})();
// (let bar 5 (foo))
// ==>
// (do (var bar 5)
// (bar qux corge))
(function () {
var g1 = env.foo.val.captures.bar;
var g2 = env.qux;
var g3 = env.corge;
return (function () {
var bar = 5;
return (0, g1.val)( g2.val, g3.val );
})();
})();
(The calls to nulan.macro_() and nulan.plus_() are just placeholders for whatever you would use there.)
Since I'm not using separate JavaScript variables for separate definitions, the original bar is totally out of scope in (let bar 5 (foo)), and I end up having to smuggle it in as part of foo (env.foo.val.captures.bar). I did this in Penknife, and I called the foo->bar path a "lexid." If I had to do it again, I might consider generating unique variables outside every environment, something like what you're doing.
For an easier solution all around, we can call the 'foo macro at run time, rather than relying on the compiler to expand it beforehand.
// (let bar 5 (foo))
// ==>
// (do (var bar 5)
// (bar qux corge))
nulan.eval_( env, [ "let", "bar", 5, [ "foo" ] ] );
This way, 'foo will return its own view of [ bar, qux, corge ], and we don't have to worry about bar being in scope.
As a bonus, this approach allows side effects during macroexpansion to work properly, rather than being forgotten by the compilation process.
The reason I didn't do this in Penknife is because Penknife macros suppressed parsing, the most expensive step, so this kind of compilation wouldn't have done any good.
---
"Anyways, with that out of the way, I actually think I'm going to go for strategy #2, because I only lose a tiny bit of flexibility, but it should be much much faster."
Just wait until someone comes along and thinks your language is too static. ;)
That approach is also good for debugging. Compiling to so-called idiomatic JS is a sacrifice many JS-targeting languages make right now.
---
"I'd still like to hear rocketnia's advice on bytecode, though, for future reference."
I've only done a few things with bytecode, most of which involve reverse engineering GB/NES/SNES machine code and game-specific binary data. I've taken a look at bytecode generation for the JVM a few times, but I know better than to trouble myself with that. :-p
Bytecode is a term commonly used to describe binary formats for programs that run on virtual machines. It's designed to be computationally cheap to execute (e.g. JIT-compile) on multiple CPU machine code dialects.
JavaScript is the only "machine" code you're targeting, so there's not much need to build a compatibility layer. However, you might be interested in fine-tuning for particular JS engines' strengths and weaknesses. For instance, supposedly V8 compiles JavaScript a lot like C code, so it might encourage large blocks of imperative code, few variables, and heavier use of structured control flow than function calls. (Eh, I almost never write C code, so what do I know?)
"Comments are versatile. Perhaps we need two kinds of comments that can be colored differently."
This exactly. If you expect programmers to read and edit code under a particular kind of interface, such as text with a particular kind of syntax highlighting, it would make sense to judge readability in terms of that interface.
Anyway, the two-comment idea seems to happen for me already! I always write "NOTE:" or "TODO:" when my comment isn't just a narrative explanation or other straightforward hand-holding. In Java and JavaScript, TODO (if not NOTE) is commonly highlighted in a more prominent color.
Even without highlighting, I think it helps that my "NOTE:" and "TODO:" labels are in capital letters. :)
While the idea may exist in practice (at least for me), that doesn't mean the language can't take it further. IDEs with code folding support tend to support folding of comments too. A language with long-form and short-form comment syntaxes might provide an option to toggle all long-form comments within a single block of code, while leaving the short-form comments alone. (This might just make it easier for people to forget to keep the comments up to date, but maybe code edits could trigger a red highlight around the comments as a reminder.)
---
"Are there still other uses for [comments]?"
The uselessness of comments often makes them useful, in a self-defeating cycle. :-p http://c2.com/cgi/wiki?HotComments But I guess we're focused on different ways comments can be used for documentation.
Javadoc, Docco, and inweb handle HTML, Markdown, and TeX in comments. Unlike syntax highlighting, these tools have a compile step (and may erase details that are visible during maintenance), so there can be a struggle between readability at the maintenance side and readability at the publication side. Last time I used JsDoc, I think I wrote at least one comment describing how the formatting worked for another comment.
Mathematica goes to the trouble to provide rich visual notation for code itself, but it doesn't appear to innovate in the realm of rich WYSIWYG comment formatting. It just has monospaced inline comments and the ability to render "text cells" of explicit markup:
Meanwhile, Inform 7 code is enough like English that it might as well be edited in a word processor. A language IDE could potentially start with the comprehensive text-managing complexity of a word processor, and then add code-specific functionality like error highlighting, autocompletion, and interactive visualizations.
(Hopefully those code-specific features will clarify the shape of the formal model underneath, rather than just showing layer upon layer of sugar, but I digress.)
Yeah, this is pretty obviously true when you consider how limited our human brains are: we just can't keep all that information in our heads at once, and so since comments aren't critical for actually running the program, it makes sense to filter them out mentally.
For me personally, I normally never write comments. When I do, it's only for three reasons:
---
1) To provide a reminder for things that need to be fixed later. These always contain "TODO" at the front, like this:
// TODO: replace foo with bar
I use a lot of these. I find it's a lot better than keeping a separate TODO list. Unit tests would work too, but I've gotten into the bad(?) habit of using unit tests sparingly, only for tricky corner cases, rather than having full code coverage.
---
2) If I'm writing a library that I expect other people to use/modify/whatever, I'll comment anything that I consider to be really important for using the library.
For instance, in the Pratt parser that I wrote last night (in JavaScript), I used comments like this:
// Converts any array-like object into an iterator
// <n.expression>, <n.parse>, and <n.parse1> all expect an iterator
// Expects an iterator of tokens and an optional priority
// Parses tokens until it finds one whose priority is lower than or equal to <i>
// Returns the parsed expression
// Expects an iterator of tokens
// Returns a list of all the parsed expressions in the iterator
// Creates a token that throws an error when used
// This is useful for placeholder tokens, like the ending pair of braces
// Creates tokens for braces
// Example usage:
//
// var t = new PARSE.Tokens()
// t.braces("(", ")")
// t.braces("[", "]")
// t.braces("{", "}")
//
I tried to keep them as short as I could, and only included the information people needed to know, like how to actually use the library, rather than providing in detail how the algorithm works. The code should be simple enough that you can study it to see the algorithm, so the comments are detailing high-level stuff.
And I especially tried to only give examples of usage when necessary, because examples take up a lot of space. So my commenting philosophy seems to lean toward "don't comment unless it's important, and try to keep comments as short as you can".
---
3) If I'm writing internal code that only I use, I'll very rarely put in a comment that explains something tricky:
// This is needed because of foobarqux
But this is quite rare because only I see these comments, so the only time I put them in is if the code is so complicated that even I can't understand it.
Because most of the code I write is in Arc/Racket and is for my own use, the only comments I write are of type #1, and very rarely type #3.
---
So my commenting philosophy is flexible: when I expect other people to use something, I'll provide a high-level comment explaining how to use it. But if it's just internal stuff that only I use, there's no need to provide comments.
That's interesting: so the more one attempts to create signal the more likely it becomes just noise.
If I were to use comments I think I would agree with having very few and making them fairly silent. Personally, though, I've abandoned comments all together. Aside from the fact that comments can easily become stale, old and even misleading, I do believe any effort put in to commenting is better spent making the code more readable or even creating unit tests as a means of telling the actual story.
All that said, I'm really strained for time plus I'm also the only one who reads my code!!!!
I just realized something... Nulan's macro system won't work in JS. The reason is because... well, first, here's a macro:
(mac foo -> {bar qux corge})
This macro will replace (foo) with (bar qux corge). The key thing to note is that it uses the values of bar/qux/corge, not the symbols. Therefore, Nulan's macro system is completely hygienic, just like Kernel.
That works just fantastic in Racket, where you can splice arbitrary things into the AST, but that won't work in JS, where things generally have to be given a name.
I could, of course, change it so that it expands to the symbols bar, qux, and corge, and then it would work in JS... but... ew, lack of hygiene. It just wouldn't be the same language anymore.
But then I realized something else... to explain this, I'll need to explain how the Nulan compiler works. Firstly, Nulan doesn't have much of a concept of "symbol": macros expand to values (not symbols), quote is discouraged, etc.
But Nulan takes it a step further. The Nulan compiler will actually remove all symbols from the AST before evaling it in Racket.
How this works is, at compile-time, the compiler has this big dictionary which represents the environment. This environment maps symbols to mutable/immutable boxes.
If the symbol refers to an immutable box, the compiler will unbox it and splice it in directly. If it's a mutable box, it'll put the box into the AST, which will be unboxed at runtime.
This is faaaaaaaast. There's no symbol lookup, not even for globals, thanks to Nulan's hyper-static scope.
Because of this, I'm pretty sure Nulan will end up being significantly faster than Arc. I don't think it'll be faster than Racket though, because Racket's modules are lexical, so I'm sure Racket can do the same sort of optimization. I do think it'll allow Nulan to be just as fast as Racket even though Nulan doesn't use Racket's modules.
Anyways, the point is, symbols are bound to boxes, not to values. And then these boxes are inserted into the AST. That way you can have the same symbol with different meaning:
The above first creates a new box and binds it to "foo". Within the function "bar", the compiler replaces the symbol "foo" with the box.
Then it creates a new box and binds it to "foo", but the compiler has already replaced the "foo" inside the function "bar" with a box, so there's no problem, and thus the call to (bar) returns 6, rather than 8.
---
Okay, that works faaaantastic in Racket, but JS doesn't have boxes, and you can't inline values into the AST anyways. So what do I do? Well, there's a little trick I'm using...
I think I'll be able to explain it easiest just by showing what the above compiles into in JS:
var foo = 1;
var bar = function () { return foo + 5 };
var foo2 = 2;
bar();
Notice that the second call to "var" created a "foo2" variable, not a "foo" variable. If you created a third "foo" variable, it'd be "foo3", etc.
Effectively, it's saving all the different "versions" of the foo variable, so that each variable is unique. And so, that's how I simulate Racket's boxes in JS.
This has another nifty side effect: on the JavaScript side, you can choose which version of the variable to call. So this gives the benefits of hyper-static scope not only to Nulan code, but also to JavaScript code that calls Nulan code.
So, going back to the macro example way at the top. Consider this:
(let bar 5 (foo))
Let's macro-expand it:
(do (var bar 5)
(bar qux corge))
In Arc, this would cause a name collision, oh noes, lack of hygiene! But what does Nulan compile it to?
var bar2 = 5;
bar(qux, corge);
Awesome, the macro remembered which version of "bar" it's using, and the variable "bar" in the "let" is replaced with "bar2". So there's no hygiene violation. With this system of dictionaries mapping symbols to symbols, I simulate boxes, and this makes Nulan-style macros hygienic, even in JS which doesn't let you insert values into the AST.
Anyways, with that out of the way, I actually think I'm going to go for strategy #2, because I only lose a tiny bit of flexibility, but it should be much much faster.
---
I'd still like to hear rocketnia's advice on bytecode, though, for future reference.
I still think it looks fine. And yeah, I'm not talking about wart in specific: Nulan hardcodes + as an infix, so it won't work nearly as well with wart's system.
I've found that all of the things I really like about Racket are simply because it's a Lisp: it would be just as easy in Arc. I bet it'd also be just as easy in other Schemes or Common Lisp.
The primary benefit of Racket over JS is that: Racket represents programs as lists. These lists can have arbitrary stuff put inside them (like actual functions). Racket is highly optimized and executes code very fast. Racket also optimizes common functional-programming patterns, like the "let" macro in Arc.
But that isn't really tied specifically to Racket. As for Racket-specific stuff... I guess the fact it has nice immutable hash tables and boxes built-in? That's about the only nice Racket-specific thing I've found so far.
So I'm not really saying "Racket is awesome", I'm really saying "JS sucks, and I wish I could eval a JS AST rather than a JS string". But yes, it is cool that Racket is a Lisp, and so it makes code compilation easier than non-Lisps.
You do have that in combination with indent sensitivity. But you can't rely on it with compound exprs and outer parens. And I still think it's klunky. Try it without the quote, or with nested parens in the operands.
"Punning functions for different meanings is fine if done tastefully. In particular, + for concatenation is fine."
The reason I'm okay with using + for concatenation is because I intuitively accept addition as a special case of concatenation. Concatenation is all + does.
However, I'm not as sure about this as I used to be. I can list a few reasons for separating addition from concatenation, and indeed for separating all kinds of seemingly polymorphic operators:
- Just because one operator gives us three context-appropriate kinds of concatenation doesn't mean we can define another operator that gives us the context-appropriate identity element (() or "" or 0).
- Addition tends to promise more algebraic properties than concatenation does (e.g. commutativity), so it may be easier to trust the behavior of an addition operator if it doesn't have concatenation weighing it down.
- Among numbers, there are multiple associative operators with identity, including addition, multiplication, and max (on the extended real number line), and I'm not sure any of these stands out as a natural choice for "concatenation."
---
"The infix version [...] is far inferior[...]"
I don't see that much inferiority. Maybe I've been working in JavaScript too long. Maybe I'm just making excuses to disagree with you. :-p
---
"Given that I want to resist the temptation to use infix + on lists and strings, I'm inclined to continue supporting join, and only use + for the easiest case"
Well, if I saw code that used 'join, I would be tempted to turn it into +, especially when it saves on indentation. YMMV.