"..indentation is not affected by the lengths of identifiers. It’s silly to format code in such a way that you need to re-indent just because you renamed a function."
That silliness is hard to avoid. If we don't use word wrap, we have to re-indent code when an identifier changes just so things stay on the screen. To avoid that re-indentation, we should probably redesign our text editors, diff tools, and languages so word wrap isn't a pain to work with. I think I'd rather do a little re-indentation for now.
---
It's not quite the same topic, but for a long time,[1] I've considered indentation an enemy to brevity. All that wasted whitespace! :) Arc's a:b ssyntax is great at eliminating levels of indentation.
My favorite comment on the HN thread[1] so far points out a new design tradeoff rather than retreading tired aesthetic preferences:
"..indentation is not affected by the lengths of identifiers. It’s silly to format code in such a way that you need to re-indent just because you renamed a function."http://news.ycombinator.com/item?id=4993847
Wow... thanks for the awk history lesson. I didn't learn awk until about 10 years ago, but now it's one of my favorite programming languages. It's a fine example of a domain specific language.
My experience with lisp is basically limited to scheme, which is a major bias, I'm sure. I get the feeling that common lisp is a bit friendlier, and makes things like user-defined macros a bit easier. The scheme documentation on the web is not all that user-friendly, and I'm forced to confess that I've never created a macro.
The pain of letting your baby go, the cost of being a control freak, weighing the potential upside of open source and benevolent dictatorships - all human dilemmas that existed way before the internet. ;)
You'll have to use the "Reset Tutorial" button to see all the changes.
The biggest change is that you can now click on an expression to highlight it. To see what I mean, try clicking on stuff in the tutorial.
But the coolest part is that the highlighting understands boxes. Try inputting this:
box foo = 5
foo + 10
box foo = 20
foo + 30
Now try clicking on the 1st or 2nd "foo". Then try clicking on the 3rd or 4th "foo". And it's so smart, it can even differentiate local variables (which is non-trivial because Nulan compiles top-level forms, like Arc):
Once again, try clicking the 1st or 2nd "foo", then the 3rd or 4th "foo".
It's also smart enough to know that the "." syntax is a string, even though it looks like a symbol:
box foo = []
foo.bar <= 5
foo.bar
So, obviously this isn't just a simple name search. The nice thing about this is, because Nulan uses boxes, it will only highlight the symbols that evaluate to the same box.
Imagine a search/replace function that operates on boxes rather than symbols: you'll be able to find/rename exactly the right symbols, rather than symbols that happen to have the same name.
Ah, there's an idea. Never thought to use the technique for avoiding slashed names. It makes sense. When you start writing Lisp macros, you quickly notice that you can use uninterpreted symbols as syntax. I remember way back when (I think probably when I first read Comprehending Monads by Philip Wadler), I toyed with writing a list macro to support not just the usual (list x), but also list comprehensions, like
(list (+ x 1) for x in xs if (> x 5))
That was a fun one. I don't think I have the code for it lying around anywhere. Not that it'd be hard to write again---it translates straightforwardly into maps & keeps. I suppose the end of this slippery slope would be the loop macro (http://clisp.hg.sourceforge.net/hgweb/clisp/clisp/file/tip/s...). Not necessarily a bad thing, if you aren't eschewing specialized syntax anyway.
Although, if the sole purpose here is to do away with slashed names, the only real place they're used in Arc is to shorten with- to w/.
So, though the mechanism by which it works is more general, you could stay disciplined by focusing on with instead of mac2 stuff. Something like the following, only in wart instead of Arc. :)
; Make 'let not rely on 'with, so it won't dispatch on special keywords by
; mistake
(mac let (var val . body)
`((fn (,var) ,@body) ,val))
(= special-with* (table))
(mac with args
(aif (special-with* (car args))
(apply it (cdr args))
(let (parms . body) args
`((fn ,(map1 car (pair parms))
,@body)
,@(map1 cadr (pair parms))))))
(mac withify (keyword parms . body) ; for want of a better name...
`(= (special-with* ',keyword) (fn ,parms ,@body)))
(withify uniq (vars . body)
`((fn ,vars ,@body) ,@(map1 [uniq] vars)))
(withify tmpfile (f . body)
`(let ,f (tmpfile)
(before (system:join "rm " ,f)
,@body)))
arc> (macex '(with (x 1 y 2) (+ x y)))
((fn (x y) (+ x y)) 1 2)
arc> (macex '(with uniq (x y) (do-something-with x y)))
((fn (x y) (do-something-with x y)) gs1740 gs1741)
arc> (macex '(with tmpfile foo (writefile 'stuff foo)))
((fn (foo) (before (system:join "rm " foo) (writefile (quote stuff) foo))) (tmpfile))
I'm not sure if that alleviates your keyword argument woes. It makes the definitions of with variants explicit without needing sigils or whatnot (because what else are you going to pass as the first argument to withify?). But, it also doesn't get along so well (aesthetically) with Scheme-style name-goes-in-arglist definitions.
They might be, once I make them user-customizable. At least, as "first-class" as macros.
---
"If not, I was thinking aloud that Nulan's parser might be able to subsume patterns somehow"
Well, the way I designed it, Nulan's parser is stand-alone: you can run it without any of the other Nulan stuff.
That's because the parser is really very simple. It's only concerned about lists, symbols, numbers, and strings. It has no knowledge of boxes, or macros, or anything like that.
This is by design. Although it would be possible to integrate the parser more closely into Nulan, I like the clean separation of concerns.
Patterns are currently executed during the macro expansion phase, so that's already long after the parser has run. In addition, while the parser operates everywhere, patterns can only be used in certain places.
How they work is pretty simple. Using the not-yet-created $pattern-rule macro:
$pattern-rule foo -> {_ pat} val body
'val + pat + body
Let's look at this program:
(-> (foo 5) 10) 20
We're creating a function "-> (foo 5) 10" and then calling it with the argument 20.
Now, it will call the "foo" pattern with the following arguments: (foo 5), a unique variable, and 10. These represent the pattern, the argument to the function, and the body, respectively. Whatever the pattern returns is used as the body of the function.
After running the pattern, the end result is this:
(-> u (u + 5 + 10)) 20
Let's look at another custom pattern:
$pattern-rule and -> {_ @pat} val body
pat.reduce
-> x y
pattern-match y val x
body
This time we're calling the "pattern-match" function, which allows the pattern matching to be recursive.
Using this system, it's easy to do, for instance, list destructuring:
$pattern-rule list -> {_ @pat} val body
w/box i = -1
pat.reduce
-> x y
pattern-match y ('val[,(++ i)]) x
body
And now this program:
-> (list a b c)
prn a b c
Will get translated into this:
-> u
| box a = u.0
| box b = u.1
| box c = u.2
| prn a b c
Though, the above "list" pattern doesn't support the @ splicing syntax, which is much more complicated to implement.
Patterns aren't first-class in haskell. Are they first-class in Nulan? If not, I was thinking aloud that Nulan's parser might be able to subsume patterns somehow.
"I wonder if there's a cleaner way to integrate it completely with the operator-precedence parser."
I assume you're talking about Nulan since as far as I know, wart doesn't have a full-blown parser. In which case it works just fine in Nulan, because the parser runs before everything else (including macros).
I'm not sure what you mean. Wart already uses macro-like vaus so it already uses implicit evaluation rather than explicit like in Kernel. And what else could quote mean in an argument list other than to distinguish variables from literal symbols?
"Now that I think about it, I wonder if it would be possible to hack something up so you no longer need w/uniq..."
After thinking about it some more, I realized it won't work very well. It would require me to make all variables global, which would slow things down and be more verbose.
"Why not just use actual pattern matching for the function arguments, like Nulan does? You can still use function-name-based lookup for calls. Then it would only be the arguments of the function that are pattern matched."
Yeah, the combination of permitting arbitrary things in function position and haskell-style pattern-matching allows us to emulate full-scale pattern matching. I hadn't noticed that before. I wonder if there's a cleaner way to integrate it completely with the operator-precedence parser.
Thanks. It sounds like the basic question is: is selective evaluation useful enough to merit using up quotes inside param lists? Because if we didn't have it, we'd be able to use quotes for pattern matching.
Well, I think you already know my answer, since I think Nulan's syntax system is superior.
But at least you're coming up with new ideas. I can't knock you for that.
---
"I'm using keyword args in the definition of multi-word macros to evoke pattern-matching, showing what parts of a call are 'constants'. (Perhaps I need to move to a pattern-matching rather than function-name-based lookup for calls?)"
Why not just use actual pattern matching for the function arguments, like Nulan does?
I would like to point out that you can still use function-name-based lookup for calls. Then it would only be the arguments of the function that are pattern matched. That's what Nulan does.
Then you could say this:
mac (with 'nils vars ... body)
`((fn ,vars ,@body))
mac (with 'tmpfile f ... body)
`(let ,f (tmpfile)
(before (system:join "rm " ,f)
,@body))
Notice that I'm using "quote" directly in the argument list, and that I used plain-old "mac", rather than "mac2". This also lets you place the hard-coded symbol anywhere you want:
mac (with vars 'nils ... body)
`((fn ,vars ,@body))
Look, ma, no need for "mac3"!
This also lets you pattern match on keywords too, if you wanted to:
mac (with :nils vars ... body)
`((fn ,vars ,@body))
mac (with :tmpfile f ... body)
`(let ,f (tmpfile)
(before (system:join "rm " ,f)
,@body))
Basically, I don't see the point of having a special "mac2" macro. I'd just use full-blown pattern matching. It's not that hard (after you've bashed your head on the wall for a while trying to understand it, okay maybe it is pretty hard), and it's much much cleaner.
In fact, depending on how you've implemented wart, you could even add in pattern matching in wart itself, so that this:
mac (foo 'bar)
...
Would be macro-expanded to this:
mac (foo $u) :case ($u = 'bar)
...
This is similar to how you already "extend" the functionality of existing stuff.
"if-box is a kinda contrived example. Perhaps we can find something better?"
If you have a better suggestion, I'm all ears. But I settled on that because A) it's simple, B) it's short, C) it's something that does actually come up in practice, so it's not too contrived.
---
"What's the difference between w/uniq and w/box? Both seem to create boxes."
Well, to explain this... unique variables are actually implemented as anonymous boxes. So implementation-wise, there's not much difference.
The difference is in the actual macros "w/box" and "w/uniq". Here's the definition for them:
$mac w/box -> @args body
'w/new-scope
| var ,@args
| body
$mac w/uniq -> @args body
'w/box ,@(args.map -> x 'x = make-uniq;)
body
The answer is that "quote" inserts boxes for global symbols, but values for local symbols. That is, this macro...
$mac foo ->
w/box bar = 5
'bar + 2
...returns "5 + 2", not "#<box bar> + 2". This is an unfortunate inconsistency which happens because I'm using JavaScript variables for the sake of speed.
I thought about having it throw an error for local symbols, which would have required you to write the macro like this:
$mac foo ->
w/box bar = 5
',bar + 2
But I decided that it wasn't worth it. Now that I think about it, I wonder if it would be possible to hack something up so you no longer need w/uniq...
---
"I tried running this and got the following"
It works fine for me. And in fact, the code snippet you posted seems exactly the same as what is already in the tutorial. Maybe you intended to paste something else?
As for the error... yeah, there are still some cryptic errors. I plan to make them more meaningful later.
I find programming to fractally involve debugging all the time. So if I allowed warnings when debugging I'd be dead :)
You're right that there are exceptions. I think of warnings as something to indulge in in the short term. The extreme short-term; I try very hard not to ever commit a patch that causes warnings. It really isn't that hard in the moment, and the cost rises steeply thereafter.
Incidentally, I'm only this draconian with C/C++. Given their utterly insane notions of undefined behavior I think it behooves us to stay where the light shines brightest. Whether we agree with individual warning types or not, it's easier to just say no.
But with other languages, converting errors to warnings is a good thing in general. Go, for example, goes overboard by not permitting one to define unused variables.
rocketnia: ..Larry Wall said "Things that are different should look different"..
In the beginning, Perl was a simple language. It generalized awk with user-defined functions[1], explicit file handles (STDIN, FILE) and array variables. With the idea that different things should look different, you got sigils to namespace arrays and scalars away from keywords and the standard library[2].
Over time, however, it got more line-noise. % for hashes. & and -> for references. Constants like $_ got added, positional arguments. When you consider all the primitives and capabilities in the language today, do array/scalar variables really deserve their sigils? It feels a little like hungarian notation now that the standard library includes so much more than just functions on strings and arrays.
It's all very well to say "different things should look different", but "different" isn't some absolute property. Programming languages are human things, and subject to human limitations. Our visual and frontal cortex gets swamped by too much "difference". So we need to pick carefully what to make salient. And, above all, not paint ourselves into a corner with our early decisions.
rocketnia: "I think the parentheses help programmers realize that they should look only at the variable names to determine meaningful differences between things."
nburns: "The kind of readability I'm talking about is at a much smaller scale, like being able to recognize a function call or a loop."
The advantage of lisp isn't necessarily that it forces you to do without syntax entirely. Perhaps it is that it allows syntax to be tuned by project/codebase, to make decisions in the small based on the characteristics of the whole.
For several months now I've been sporadically mulling this rambling paper: http://davewest.us/pdfs/ducks.pdf. I'm not sure what it's trying to say, but the lesson that sticks to my mind is that of the Tibetan Thangka, arranging a number of stories spatially for maximum memetic power. A program doesn't have to be just a list of functions, macros, symbols and calls. Or indeed a long list of user stories. I'm starting to believe that it matters how things are arranged in the small, because it can help grasp how things are arranged in the large, help the program to get into my head, ensure I understand how it is organized in the large, keep me from messing up the architecture with my changes, and thereby help preserve the program's coherence over longer periods of time.
[1] nawk -- the first version of awk with user-defined functions -- was released in 1988 (http://en.wikipedia.org/wiki/AWK). Larry Wall had already released Perl in 1987.
Finally it works. I have decided to switch to nginx and with only few tweaks, I can now run on 80 port, the default port of nginx.
Here is the simple code to do that on etc/nginx/nginx.conf
server {
listen 80;
server_name bloggervietnam.com www.bloggervietnam.com;
location / {
proxy_pass http://127.0.0.1:8080;
}
Remember to put this code inside http tag otherwise it will cause error. You can check nginx configuration by typing : nginx -t and then restart nginx. It works!
Firstly, there's now an interactive tutorial. Just open up "nulan.html", and if you don't see the tutorial, click the "Reset Tutorial" button at the top. Be careful, it'll clobber any changes you've made.
Secondly, you might have noticed that the editor has syntax highlighting now. But it's actually really cool. First, try inputting this:
$mac foo -> a b c
a + b + c
foo 1 2 3
def foo -> a b c
a + b + c
foo 1 2 3
Notice that it colored the first "foo" green because it's a macro, and the second "foo" orange because it's a function. It also colored local variables blue.
Now try inputting this:
# Array comprehensions
box in
$mac for -> x {('in) n y}
'y.map -> n x
$syntax-infix for 0 [ order "right" ]
$syntax-infix in 0 [ order "right" ]
(x + 2) for x in {1 2 3}
Notice that syntax is colored grey. Not just the built-in syntax, but user-created syntax is colored grey as well! It also understands delimiters:
$mac ^ -> a b
a + b
$syntax-infix ^ 0 [ delimiter %t ]
prn 1^2
Lastly, you can use "Debug mode" to toggle whether additional information is displayed in the lower-right panel or not.
1. I just install screen and fortunately it works well. I just hit F6 button to detach my terminal session. That is why I removed fluxbox GUI for saving RAM and resource.
2. I follow the help above and now I can chance the color of the forum.
3. I have not solved the matter yet since apache did not work despite that I already installed necessary modules as you mentioned.
I may switch to nginx for a try. Thanks for your care again :-)
>> Then I learned JavaScript, and I no longer had to worry about choosing specific numbers for my array sizes or fumbling with pointer operators.
Javascript is easier in that way because it has garbage collection. Garbage collection makes programming easier. But you can't add garbage collection to every programming language. There are trade-offs when the language abstracts away details like memory management.
>> I suspect C feels close to the hardware because... CPU-scale architecture design has continued to pander to it
I think this gets to the issue of the Von Neumann architecture, and the fact that it isn't the only possible way to design a CPU. I'm not well educated on this subject... However, I don't think you can say that C is the reason for the Von Neumann architecture. I think it's the other way around.
>> "a pointer is the same thing as a memory address"
>> Eh? Who says it isn't? :)
I meant that pointers are the same as integers, integers that hold memory addresses. Which is what they are from the standpoint of the cpu.
I think that's a good practice in general. But when you are experimenting and debugging, it can be useful to eliminate chunks of code by expedient means, which often generates warnings that you don't care about.
>> >> "Consider the keywords "private" and "protected;" they literally have no effect other than to cause compile-time errors."
>>
>> Would you still consider this semantics restrictive if the default were private scope and a programmer could intentionally expose API functionality using "package," "protected," and "public"?
Actually, in C++ the default for class members is private...
It's simply a true statement that "private" generates no machine language. All it does is cause compilation to fail. Whether or not this is a good thing is a matter of opinion.
>> IMO, anonymous functions make OO-style private scope easy and implicit, without feeling like a limitation on the programmer.
If you're speaking of lexical closures, I think you're right. You don't need to declare variables as private, because you can use the rules of scoping to make them impossible to refer to. You can achieve the same thing with a simpler syntax and more succinct code.
>> I believe you're assuming a program must run Turing-complete computations at run time. While Turing-completeness is an extremely common feature of programming languages, not all languages encourage it, especially not if their type system is used for theorem proving.
I'm not assuming that programming languages must be Turing complete. It happens to be true of all general-purpose languages that are in common use today.
>> Functional programming with guaranteed termination is known as total FP. "Epigram [a total FP language] has more static information than we know what to do with." http://strictlypositive.org/publications.html
I'll take a look at that language. I think that in 50 years' time, we might all be using non-Turing-complete languages. Making a language Turing complete is the easiest way to ensure that it can solve any problem, but isn't necessarily the best way.
( Technically, a language has to be Turing complete to solve literally any problem, but my hunch is that all problems of practical utility can be solved without resorting to a Turing machine.)