Arc Forumnew | comments | leaders | submitlogin

Sorry, I didn't see this for a while.

I didn't really mean to say that the book was directly applicable to the problem at hand; I was thinking of throwing in a disclaimer, but I guess I didn't. I don't have the book handy, but as I recall, some of the principles were things like grouping similar things together, and making things that are different look as different as possible. The author goes on to describe how these things determine where your eyes land and then move over the page, which consequently determines how easy or hard it is to make sense of; remarkably, it was true, as you can see from the examples. The insight was striking... I'm not sure how much of my current thinking was in the book and how much is extrapolation; but I think it's possible to bring the mindset of a graphic designer to something like code. I like how in PHP, variable references always have dollar signs attached. If you bring the mindset of a computer scientist only, you might say, well, the dollar signs are obviously to simplify the tokenization; if you can solve this engineering problem, you should get rid of them. But I think it probably makes a difference in helping your brain's "tokenizer." The connection to the design book might be tenuous, but it seems to me that you could connect it to the idea of making different things look different, or one of the other principles in the book. A minimalist implementation of lisp has practically no inherent structure, which is both a strength and a weakness. Before I knew anything about how to write lisp programs, I knew that lisp had lots and lots of parentheses. I think that if you ask programmers what they know about lisp, probably 10% have used it, and 90% know that it has lots of parentheses. This is the thing that stands out when you look at it.

I think that book is a good book, just in general, and probably not a waste of time to read. But I admit that a leap of imagination is required to see applications to programming language design. Maybe multiple leaps.


In that line of reasoning, I've been thinking about adding in a static type checker to Nulan. But I want it to use a blacklist approach rather than a whitelist.

What I mean by that is, if it can be guaranteed at compile time that a program is in error, then it should throw a well formatted and precise error that makes it easy to fix the problem.

But if there's a chance that the program is correct, the type system should allow for it. This is the opposite of the stance in Haskell/SML which says: if it cannot be guaranteed at compile-time that a program is valid, then the program is rejected.

Here's an example of what I'm talking about:

  def foo ->
    bar 10 20
The variable `bar` isn't defined. This can be determined at compile-time. Thus, Nulan throws this error at compile-time:

  NULAN.Error: undefined variable: bar
    bar 10 20  (line 2, column 3)
    ^^^
The error message is precise, and pinpoints the exact source of the error, making it easy to fix. And likewise, this program...

  def foo -> 1
    5
   
  foo 2
...creates a function `foo` that requires that its first argument is the number `1`. It then calls the function with the number `2`. This situation can be determined at compile-time, and so I would like for Nulan to throw this error:

  NULAN.Error: expected 1 but got 2
    foo 2  (line 4, column 5)
        ^
But with this program...

  def foo -> 1
    5
   
  foo a + b
...it might not be possible to determine whether the first argument to `foo` is the number `1` or not. If this were Haskell/SML, it might refuse to run the program. But in Nulan, I would simply defer the check to runtime.

This means that every program that is valid at runtime is also valid according to the type-checker. Thus the type-checker is seen as a useful tool to help catch some errors at compile-time, unlike Haskell/SML which attempt to catch all errors at compile-time.

I think this kind of hybrid system is better than pure dynamic/pure static typing.


Yes I pretty much agree with what you are saying.

---

"How do you feel about Lisp versus SML?"

I've never used SML and have only read a little about it. It looks a lot like Haskell, which I don't have much experience with either.

But from what I've seen, I don't like static type systems. I think making them optional is fantastic, but I don't like having them shoved down your throat.

I think you should be able to write your program in care-free Ruby/Arc style, and then once things have settled down, go back in and add type annotations for speed/safety. But you shouldn't have to use the type system right from the start.

The problem is that a lot of the people who find static type systems useful are also the kind of people who like safety a lot, so they want the type system on all the time. Not just to protect their code, but to prevent other programmers from making mistakes.

I don't like that mindset. Which is why I prefer languages like Ruby and Arc, even with their flaws. I don't think any restriction should be added in to prevent stupid people from doing stupid things. I think the language should only add in restrictions if it helps the smart people to do smart things. And for no other reason.

So as long as the type system helps smart people to do smart things, and doesn't get in the way too much, then sure, I think it's great. But if it gets in the way, or it's done to prevent stupid people from doing stupid things... no thanks.


I didn't say high-level is bad. The biggest counter-example to that I can think of is pure mathematics. Clearly, mathematics is incredibly useful.

It just seems to me that, more often than not, the orthodoxy with respect to programming is wrong. Or, at best, partly wrong.

Another thing about programming languages designed for beginners is that they tend to be very strongly typed, e.g. Basic and Pascal. This is either because the designers thought it made things easier, or perhaps because they thought it instills some useful lesson. As far as making things easier, that doesn't seem to be true: Think of how many people out there that would flunk out of CS school are managing to write javascript and PHP.

Leaky abstractions is exactly right. At some point, all abstractions seem to break down. This is why I'm not sure the OO principle of encapsulation or information hiding is a good thing. The more you hide the implementation of something, the more you are forcing the use of an abstraction, and the harder it will be to go around the abstraction when, inevitably, you have to.

I don't think there is much you can say about programming languages without qualification. The range of programs you can write is too vast to generalize.

How do you feel about Lisp versus SML? I had to learn SML my second year in college, but at that time in my educational career I wasn't doing much studying, and I only half-learned it. I think some form of Lisp might have made a better introduction to functional programming, because you wouldn't have to deal with SML's type system at the same time.


"The thinking seems to go that the more you abstract away the hardware, the more understandable the language will be. But it seems to me that a concrete concept is easier to understand than an abstract one. The hardware gives you a concrete frame of reference."

The problem is with leaky abstractions:

http://www.joelonsoftware.com/articles/LeakyAbstractions.htm...

The reason C "feels right" is because it matches the hardware, as you said.

But that doesn't mean high-level is bad: you can have high-level hardware. I'm sure on a Lisp machine, Lisp would feel right at home, whereas C wouldn't.

Unfortunately, I predict we'll be stuck with our von Neumann hardware for quite some time, which will severely hinder the progress of our software. No such thing as a free lunch, eh?

3 points by Pauan 5072 days ago | link | parent | on: Update on Nulan

I just added in the ability to run Nulan straight in the browser:

https://github.com/Pauan/nulan/tree/01f43d4293e64b06d046fc3f...

Just open up "nulan.html" and go! It will compile+eval as you type, with the result displayed on the right side.

EDIT: Now it saves/restores your input when reloading the page.


From that link --

"One could argue that a "usability test" of Fortran II leaded to a complete new language: BASIC, which was designed to be more usable (especially for beginners) than its predecessor."

Funny. I learned BASIC first, and then the second language I learned was C, when I was about 15. I found C much more understandable than BASIC. Things that had seemed mysterious in BASIC suddenly became crystal clear in C. Like the fact that a character is the same as an integer holding its ASCII code, a string is the same thing as an array of characters -- and a pointer is the same thing as a memory address. The puzzle pieces all fit. The thinking seems to go that the more you abstract away the hardware, the more understandable the language will be. But it seems to me that a concrete concept is easier to understand than an abstract one. The hardware gives you a concrete frame of reference.

2 points by Pauan 5072 days ago | link | parent | on: Update on Nulan

After much effort (curse you JavaScript and your stupid statements!) I got all the Examples in the README working. Go on, try it out.

Now I'm working on making Nulan suitable for writing shell scripts. Then I'll rewrite my "playlist" program in Nulan. After that, I'll tackle writing a Chrome Extension in Nulan.


I just made some changes to Nulan's parser. Now almost all the syntax is customizable:

https://github.com/Pauan/nulan/commit/b13e6477726cc111af4c23...

The changes are: renamed "action" to "parse", added in a "vertical" option, added in a "tokenize" function.

---

Now, significant whitespace is hardcoded, numbers are always 0-9, and symbols are anything that isn't a number or delimiter.

But everything else is customizable. Everything. Even comments, strings, and space/newline is customizable.

---

The new "tokenize" function lets you fairly easily add in new tokenizers, which is what string/comment/space/newline does.

---

The new "vertical" option is pretty cool too. It's currently only used by "|" and it means that this:

  foo bar
    | qux 1 2 3
    | corge 4 5 6
Is parsed like this:

  {foo bar
    | {qux 1 2 3
       corge 4 5 6}}
But the "|" syntax also specifies "separator", so it's further parsed to this:

  {foo bar
    | {{qux 1 2 3}
       {corge 4 5 6}}}
After running the "parse" functions, the final result is this:

  {foo bar
    {| {qux 1 2 3}
       {corge 4 5 6}}}
By the way, "|" is just like "do" in Arc, but I think it looks a lot nicer.

Thanks! 'Pratt parsers' was precisely the phrase I was looking for :)

> > ..resolving function/macro calls to be anywhere in the list..

> Not quite.. This all happens before macros are expanded..

Yeah, that was exactly what I meant.


To fully explain how the system works, let's use this program:

  prn 10 20
    foo bar + 50 * 20 - 30
After tokenizing, we use the significant whitespace rules to wrap the stuff in lists:

  {prn 10 20
    {foo bar + 50 * 20 - 30}}
Now, for each list, we run a modified Pratt parser on it:

https://github.com/Pauan/nulan/blob/79ea2a9fee8cb1ea7640d8f5...

Yes the Pratt parser really is that tiny. Now, let's start with this list:

  {foo bar + 50 * 20 - 30}
We start parsing at a precedence level of 0. First, we take all the non-syntax rule symbols and put them into a list. Now we have these two lists:

  left:      {foo bar}
  symbol:    +
  remaining: {50 * 20 - 30}
The + syntax rule has a precedence of 70. Now we need to recursively call the Pratt parser with a precedence of 70:

  left:      {50}
  symbol:    *
  remaining: {20 - 30}
The * syntax rule has a precedence of 80, which is greater than 70, so it continues parsing:

  left:      {50}
  symbol:    *
  right:     {20}
  remaining: {- 30}
The - syntax rule has a precedence of 70, which is equal to 70, so it stops parsing. It now calls the action function for * with the arguments left, symbol, and right, which returns {* 50 20}

Now we go back to the + syntax rule, which looks like this:

  left:      {foo bar}
  symbol:    +
  right:     {* 50 20}
  remaining: {- 30}
It calls the action function for + with the arguments left, symbol, and right, which returns {foo {+ bar {* 50 20}}}:

  left:      {foo {+ bar {* 50 20}}}
  remaining: {- 30}
Now it continues parsing with a precedence of 0. - has a precedence of 70 which is greater than 0, so it recursively calls the parser with a precedence of 70:

  left:      {foo {+ bar {* 50 20}}}
  symbol:    -
  right:     {30}
  remaining: {}
It now calls the action function for - which returns {foo {- {+ bar {* 50 20}} 30}}

We now call the Pratt parser on the outer list with a precedence of 0:

  left:      {prn 10 20 {foo {- {+ bar {* 50 20}} 30}}}
  remaining: {}
There aren't any syntax rules, so it just returns it unmodified, and so the final answer is:

  {prn 10 20
    {foo {- {+ bar {* 50 20}} 30}}}
---

Pratt parsers are absolutely amazing. They're very very short, easy to implement, very fast, and incredibly flexible.

If you want to learn more, I recommend these links:

http://eli.thegreenplace.net/2010/01/02/top-down-operator-pr...

http://javascript.crockford.com/tdop/tdop.html

http://effbot.org/zone/simple-top-down-parsing.htm

https://groups.google.com/forum/?fromgroups=#!topic/comp.pro...

http://journal.stuffwithstuff.com/2011/03/19/pratt-parsers-e...

I can also clarify things further, and provide a simple stripped-down version of Nulan's parser, if that helps to understand.


"If I follow everything on that page, this allows you to programmatically control where lists are segmented [...]"

Something like that, yes.

---

"[...] resolving function/macro calls to be anywhere in the list. In other words, macro foo doesn't have to look like"

Not quite. The parser is very simple: it just pushes symbols around. Which means that when you use a syntax rule like this:

  $syntax-infix "foo"
Then the parser will rewrite this:

  1 foo 2
Into this:

  foo 1 2
This all happens before macros are expanded, so macros receive a uniform list representation. In this way, it's similar to wart's system, except that it's much more flexible and powerful.

---

"Does this make the grammar context-sensitive? Does it often introduce ambiguity? Have you run into non-terminating parsing?"

I have no idea, no, and no. It's all just simple list transformations, similar to what macros do.

---

"Can you talk about how it compares with oMeta? (Are you sure you aren't greenspunning it? :)"

I haven't use oMeta, but from what I understand, it uses something like PEG parsing, which is completely different.

Think of my system as being like macros: you take this list of symbols and that list of symbols and return this list of symbols.

The difference with macros is that my system has left/right associative, prefix/infix/suffix, precedence, and a slew of other options too.

The key insight is that unlike most syntax systems which return a single expression, Nulan's syntax system returns a list of expressions.

And then the syntax rules operate on this list, which effectively lets them look-behind and look-ahead arbitrarily many tokens, but only within the list.

PEG parsing lets you look-ahead as many tokens as you like, but not look-behind. Nulan's system supports both, but the amount of look-ahead/behind is controlled by the indentation, so everything is handled in a consistent way.


Very cool!

If I follow everything on that page, this allows you to programmatically control where lists are segmented, and to generalize infix, resolving function/macro calls to be anywhere in the list. In other words, macro foo doesn't have to look like

  (foo ...)
it can also look like

  (... foo ...)
Does this make the grammar context-sensitive? Does it often introduce ambiguity? Have you run into non-terminating parsing? Can you talk about how it compares with oMeta? (Are you sure you aren't greenspunning it? :)

I would use it.

Could "withs" be renamed to something shorter, like "vars"? Why "progn" and not "do"?


Only supports a single variable currently, though it would be cool to support multiple:

  w/each x = {1 2 3}
    prn x
Nulan's macro system is not only hygienic, but it plays very nicely with customizable syntax too, as you can see in the definition of "w/each":

  $mac w/each -> {('=) x y} body
    w/uniq i len
      w/complex y
        'w/var len = y.length
           for (var i = 0) (i ~= len) (++ i)
             w/var x = y[i]
               body
And a couple more examples:

  # Simulating a `for` loop using a `while` loop
  $mac for -> init test incr body
    '| init
     | while test
         | body
         | incr

  for (var i = 0) (i < 10) (++ i)
    prn i


  # Simulating a `do..while` loop using a `while` loop
  $mac do -> body {('while) test}
    'while %t
       | body
       | if ~ test
           &break;

  var i = 0
  do
    | prn i
    | ++ i
    while (i < 10)
P.S. Sorry for hijacking the thread, but I've been itching to talk about Nulan, since I've made so much progress on it (as shown by the many commits on Github).

Second shameless plug: I've changed Nulan significantly since I last showed it off here. Here's a page describing Nulan's syntax system, which I believe is superior to all existing Lisp syntax systems:

https://github.com/Pauan/nulan/blob/79ea2a9fee8cb1ea7640d8f5...


Nice. What is the s-exp for each?

I've considered replacing with and just keeping the serial version. But I like encouraging myself to not worry about ordering as much as possible.


Shameless plug: this is how Nulan handles it:

  # Single binding
  w/var foo = 5
    ...
  
  # Multiple bindings, like "withs" in Arc
  w/var foo = 5
        bar = 10
    ...
  
  # Look, ma, destructuring works
  w/var {foo bar} = {1 2}
    ...
3 points by rocketnia 5075 days ago | link | parent | on: QuickUI JS framework has enticing docs

I stumbled across this in an unrelated search for "meta-control." The documentation quality surprised me. Take a look at it yourself for a bit and come back. :)

---

I found myself interacting with the example before I finished reading about it. The clear section organization of "The meta code..." and "Your goal:" makes it easy to skim. The fact that the goals are very simple--just change an identifier--makes it painless to jump in and modify the example.

If I actually cared about using the system documented here, I would probably do more than change identifiers. :-p

Oops, I couldn't help myself. I put a combo box in my combo box. Broken, but wonderful! XD

3 points by akkartik 5076 days ago | link | parent | on: An editor for Scheme9

rocketnia, you'll like this make-your-adventure framework that comes with Scheme9 :)

http://www.t3x.org/s9fes/adventure.adv.html


With regard to your trailing-paren hack at http://subjectivelisp.org/index.php?title=How_code_is_organi... -- I'm curious to get your reaction to my toy interpreter: http://github.com/akkartik/wart/blob/190ced3d73/004optional_...

I've been reading http://subjectivelisp.org/wiki/Differences_with_Nu and noticed that your definition of let permits multiple variables. I'm curious if you considered the use case of destructured binding:

  (let (a b) (list 3 4)
    ..)

And Gambit has been ported to iOS and Android.

Am I interested? ... For sure.

I've used a clojure->scheme->c[1] a little, but Arc could also have it's place too.

Actually I had always wondered if, or hoped that, someone would build Arc atop gambit scheme instead of racket which, in my mind, would be the easiest path to also get Arc working on iOS, but it's not my area so maybe you can tell me which would be better.

Either way I think Arc would be a great fit for creating iOS apps as Arc has much simpler language constructs and mutability/concurrency wouldn't be as big of an issue given the localized nature of mobile code.

[1] https://github.com/takeoutweight/clojure-scheme


I tend to jump around. The introduction is usually boilerplate for the particular area of research, so it can be skipped. (I wonder how many different papers have told the story of the memory hierarchy and how it's getting more and more important as data gets bigger.) Then I try to figure out if the paper has anything important to say, before working on the math. I figure that sometimes the big idea of the paper is in the math, and other times, the big idea is in the text, and the math is just obligatory. (You can't publish a paper on an algorithm without spelling out the precise bounds on time and space, even if the formula contains 15 terms. Not all 15 terms can be important to the performance, but it certainly is important to put them in the paper.) I guess it depends on the field, but in the data structures papers I like to look at, it usually doesn't take a lot of math notation to express the key innovation.

I don't disagree with you. I was arguing for the value of well-written prose.

Donald Knuth thinks that programs should be more like prose (http://en.wikipedia.org/wiki/Literate_programming) -- not that I've ever tried, or fully understand, literate programming.


"I've been reading a lot of computer science papers lately, and I tend to skip over the math formulas and focus on the text."

I do that too. :) Unfortunately, at some point it gets hard to understand the prose without going back to read some of the fine details of the system they're talking about. XD


"But that probably has nothing to do with programming language syntax per se."

Why do you say that? Syntax is notation. Check out this paper by the guy who invented APL, when they gave him his Turing award: http://awards.acm.org/images/awards/140/articles/9147499.pdf


I seem to have overlooked this post until just now...

Incidentally, I've never written python, ruby, or haskell, except for a tiny amount of python.

Good quote. I've been reading a lot of computer science papers lately, and I tend to skip over the math formulas and focus on the text. This could be because I'm reading them for "fun" and not because I have to for a class, or something. But I have always found it hard to take in dense notation, and preferred a conceptual argument. Maybe it's just that I have a deficiency in that area. But I think prose has the potential to carry powerful insights that are out of the reach of formulas; I suspect the problem is that succinct, brilliant prose is just incredibly hard to write. It's probably easier to just list formulas than to get deep ideas into prose. The reverse is also true, of course. Some ideas can only be expressed properly with notation.

But that probably has nothing to do with programming language syntax per se.


via http://news.ycombinator.com/item?id=4909666

Example app in Tranquil: http://github.com/fjolnir/xnomad#readme

More