Hacker News new | past | comments | ask | show | jobs | submit login
A tiny language called Z (chrisdone.com)
240 points by randallsquared on Jan 1, 2013 | hide | past | favorite | 52 comments



It seems like everyone else is giving you flak so let me offer some words of encouragement:

This looks great! This really makes functional programming more readable. It forces the use of whitespace which is my favorite part about python. It lets me easily sort the arguments to each function.

Don't mind the other people telling you about smart editors, that's just a band-aid over the problem of parens.

You've come up with a better way of writing functional code, just like CoffeScript is a better way of writing JavaScript.

Now for some constructive criticism:

You might want to add a note that your implementation on Github requires Haskell.

You only have syntax highlighting for Emacs. Maybe give vim some love next?

If you develop it further you will want better documentation or people will walk away just because they can't get it started.


I also love it.

I had been designing a language in my head like this. I called it "spacy" as in space rich and a bit off the wall. (As others have noted there are similar specs/languages around)

I had been struggling with what to do about continuation lines and multiple arguments. I think you solved the second very nicely.

As for chosen indentation; let the programmer decide. The first use of space (number of) or tabs determines it for the rest of the file. Any other indentation is a syntax error.


I like fiddling around with weird languages... but...

You find it. Then you start walking forward, looking for a closing parenthesis.

This is the worst way to work with Lisp code and is unfortunately the most naive way. It happens to be the way most people immediately jump to when editing Lisp code and hence how we end up with the pernicious "parenthesis" meme.

The way most Lisp programmers handle parenthesis is by using a smart editor. The "parens" structure the code very closely to the AST of the actual program. Computers love trees and editors can therefore readily manipulate them with little effort. You rarely edit the text of a Lisp program this way and instead edit it's tree.

Note: See the 'paredit' package in emacs for an example.


I meant as _implementing_ an editor, like Paredit. If you're implementing Paredit, you need to find the parens in order to manipulate the current node. I just meant it's slightly easier to write such an editor. (∩▂∩) At least, I think it would be. I haven't tried!


Wouldn't it be easier to use some kind of formal grammar and a parser instead of the stack based thing you describe? In that case, lisp is probably one of the easiest languages to parse since it is all parenthesized s-expressions and not whitespace dependent at all.


Sure, it's fairly easy to parse. I think probably there is a speed hit (Emacs Lisp code is very slow—though you can call out to an external C library (I've tried it), for which ECL could be maybe used), but quite possibly it's just that, for Lisp, Paredit is mostly sufficient. I think the only case where it breaks down/has trouble is reader macros. I think it can consult SBCL somewhat upon encountering them, not entirely sure.


I have tried, and it's very easy (Goldhill's editor back in 1984); can't say for other languages, though.


My impression sometimes is that RPN (like in Forth) could be used as well and do with parenthesis altogether

But of course, Forth has a stack, Lisp does not need one

To write 5 * (2 + 3)

Lisp: ( * 5 (+ 3 2))

Forth (ignore the dot): 2 3 + 5 * .


Forth actually has two stacks. Lisp has garbage-collected storage and environment structures, and Forth does not need them. :)

Logo is a Lisp-like language which uses prefix notation but does not require parentheses surrounding every procedure invocation. Your example could be written as:

  product 5 sum 3 2
Parens are allowed for adding clarity and are only required for a vararg-style invocation. The main downside of this approach is that it is necessary to know the arity of every procedure in an expression to unambiguously parse it- Logo implementations typically either defer some safety checks to runtime or compile in two passes. Forth doesn't have this problem because it doesn't parse expressions per se- it compiles and interprets as it goes along.


Check out nokolisp for the opposite of your idea! http://koti.welho.com/tnoko/Nokolisp.htm


Lisp needs the parenthesis not because it lacks a stack, but because functions (like +) can take a variable number of arguments.


I made a parentheses-less Lisp-inspired language a while back by using prefix (polish) notation with fixed-arity functions. You can find it here: https://github.com/darkf/possumv2


Right-associative function application is an excellent choice. Perl has it, and I often wish for it in Haskell when writing long compositions:

    f . g . h $ i x
However, this kind of indentation rapidly gets out of hand, and requires some editor awareness to be remotely usable:

    defun map f xs
          if unit? xs
             unit
             cons f car xs
                  map f
                      cdr xs
Moreover, there doesn’t seem to be any virtue in preventing multiple indentation styles. Readability often depends not on consistency, but on redundancy, and using the right style for the situation. I would much rather read and write this:

    defun
      map f xs
      if
        unit? xs
        unit
        cons
          f car xs
          map f, cdr xs
That is, indentation would only introduce a block of arguments, and arguments on the same line could only span that line. If you wanted multiple arguments on one line, you could use a separator—here, a comma.


To be perfectly honest your re-formatting is actually worse, to my eye. Which is surprising because Z is already ugly! You've made it longer, moved the condition away from the if (that is really mental!), and added a rather spurious looking comma (which has problems that I describe below). Actually, Z looks a lot like Common Lisp or Elisp, fully normalized. Let's say “fully normalized” means: indent everything that could be, aligned with the operand. E.g. here's Z:

  defun map f xs
        if unit? xs
           unit
           cons f car xs
                map f
                    cdr xs
Here's Lisp:

  (defun map (f xs)
         (if (unit? xs)
             unit
             (cons (f (car xs))
                   (map f
                        (cdr xs)))))
I don't think any Lisper would really complain about that code if she came across it. That is actually how I would write it when coding in Elisp or Common Lisp, with two exceptions: Perhaps for the that "defun" and other def-* forms are often indented only two spaces after the parent's offset. Now, I thought about allowing that, and that's a good point, stylistically. But it seems not to change the overall z-expressions point much. To argue about needing editor support would also be to argue against Lisp or Python, in which case there are other people interested in arguing about that.

The Lisper might also complain, if she were being stringent, about the “map f …” being line-separated. And for that I'll say, ok. Too bad. Personally, I find myself line-separating arguments in Haskell and Lisp more often than not, so it's not a use-case I would feel the need to optimize.

Regardless, you can pretty much ignore all I said above, because it's just an aesthetics thing, kind of irrelevant to the core concept. The whole point of the indentation and right-associative application is for the Markdown-inspired macros: everything after the name is up for grabs for a macro (or special operator). The idea of adding a comma pretty much negates that and the whole point of z-expressions. If I could have commas, if I were to make that concession, I'd just have parentheses, and use s-expressions and reader macros.


I see what you mean. A separator doesn’t work with the concept at all. Apart from that, my reformatting still has two distinct advantages: it does not require a fixed-width typeface, and 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's true. I don't like reading it, but I would prefer writing it. It's pretty much why in Haskell I write:

  foo $ bar $ do
    hello
    world
rather than

   foo $ bar $ do hello
                  world
You could easily have:

  foo bar
    mu
be equivalent to

  foo bar
      mu
but if you had

  foo bar bob
        zot
    mu
And you rename foo, you're back to square one again (zot is no longer syntactical). Unless you just have a rule like:

  foobarmuzot bar bob
        zot
      mu
means

  foobarmuzot bar bob
                  zot
               mu
But it seems comparatively difficult to read, and deceptive.


Alexander Burger indents his PicoLisp code more along the lines you describe. Example: http://rosettacode.org/wiki/Execute_Brain****#PicoLisp


My favorite exagesis on the benefits of right-associativity: http://arclanguage.org/item?id=16820


That's what I like in Notepad++ (maybe other editors have this as well). You can view tab columns. So your indent is very clear.


There is already a language called Z, it has been around for a while: http://en.wikipedia.org/wiki/Z_notation


It is very beautiful because it seems to match quite closesly the diagrammatic interpretation of a symmetric monoidal closed category. For example because of the functoriality of the the monoidal tensor we have

(g ∘ h) ⊗ (i ∘ j) = (g ∘ i) ⊗ (h ∘ j)

so the two expressions should be indistinguishable. Indeed, in this Z syntax the expression

  k g h

    i j
could be parsed either as k ( g(h) ⊗ i(j) ) or as k( (g⊗i)(h⊗j) ) but this ambiguity does not matter since they are equivalent. I wonder if

  k g

    h
is a partial appilcation that would give you a function of the right type, i.e. the type of h ⊗ j.

Anyway, beautiful stuff.

PS. Not sure how to typeset this properly in this web site. PS. Thanks to commenter.


Funnily enough, code blocks are created by indenting with spaces. But since it is too late for you to edit your comment, here are your examples:

  k g h
      i k



  k g
      h


  k g h
      i j
Presumably?


Surely the ambiguity is likely to matter in actual programs?


There is no ambiguity in the z-expression syntax as far as I have been able to detect. Following the straight-forward rule of right-associative application:

  k g h
      i j
  →
  k (g h
       i j)
  →
  k (g h
       (i j))
In Lisp syntax:

  (k (g h (i j))


So this is, if i'm not missing anything major, lisp with indentations instead of parentheses. Funny, I spent my new year's eve writing a script to turn any Photoshop layout into an atlas with JSON metadata sufficient to rebuild the layout in code.

No regrets :-)


This is why I come to Hacker News. A cool technical idea at the top, then in the comments I discover a bunch of other cool stuff. If nothing else, I'll probably use this syntax in my hand-written notes.


Would be nice to see if this could be converted to javascript to run in the browser, just like markdown. I don't know Haskell myself, but there are javascript ports of the Parsec parser library here: https://github.com/weaver/ReParse http://code.google.com/p/jshaskell/

As well as several Haskell to JavaScript compilers here (Fay, ghcjs, & jshaskell seem to be more popular): https://github.com/jashkenas/coffee-script/wiki/List-of-lang...


FYI, chrisdone is the author of Fay.


See also the SRFI for I-Expressions, for Scheme: http://srfi.schemers.org/srfi-49/srfi-49.html


That looks a lot like http://srfi.schemers.org/srfi-49/srfi-49.html, honestly.


In 2006 someone did something very similar (although generalizing from Lisp and not starting from scratch or looking at niche applications); it was discussed on HN in 2009.

http://news.ycombinator.com/item?id=823524


I like the syntax; it feels like one I've been groping towards myself. As another comment said, it reminds me of TCL.

I'm not convinced on the string-based macros. They enable really cool stuff, like scala's XML literals, but it seems like they'd make syntax-aware editors impossible.

I'd suggest moving away from the lispy names for things (cons, cond, defun, cdr). This is (presumably) a language that appeals to people who like the elegance of lisp's reflexivity but can't stand the syntax; names from a more familiar language would appeal to them, while anyone who's firmly attached to lisp names is probably also attached to brackets.


Oooh, so it's a notation, called Z? Sounds familiar...


No, they are Z-expressions.

No-one has a problem with the S programming language co-existing with S-expressions.


Just to be sure: you're familiar with Z notation? Given that the Z from this article is special quite specifically because of its notation, choosing the name Z when there's already a "Z notation" feels a bit, ehm, odd.

Oppose this to S-expressions, which is a term not used a lot when not programming Lisp. You wrote that program in Lisp, not "with S-expressions".


Conjecture: For all characters c taken from the set of 'A' through 'Z' (inclusive), there is a notation N_c such that { http://www.google.com/search?q=c+notation } is nonempty.


Reminds me a little bit of Tcl.


Macros seriously take in a string and output another string? That has to be formatted correctly with newlines and indentation? That is bats.


How would you implement the string macro (":") or the regex ("~") macro without that?


Bats isn't a thing! It'll never be a thing!


Pretty neat, I've long wanted a Lisp that used indentation rather than unreadable parentheses, but didn't get far in trying to set it up. Good work! Next step is to make it transcompile down to a standard Lisp so it can make use of all the other existing libraries made for Lisp.


Is there an advantage to having the macros operate on strings, instead of the AST directly like lisp/scheme?


It took me a while to understand, but it's the whole point of the article: macros that can also do their own tokenizing and parsing. That enables syntax you can't have with lisp macros (including unbalanced parens!) It's still unclear if it enables anything useful, but still a cool idea.

(I have an interest in this space: http://github.com/akkartik/wart#readme)


Common Lisp does have reader macros, which change how code is parsed into ASTs


The comments are the coolest part of this idea. But there's nothing I care more about syntax-highlighting, so in practice I'd just say:

  -- lorem ipsum
  -- quidquid dior
  defun ...


I kinda like the idea but...

As as been pointed out, it's pretty much a Lisp where parenthesis have been replaced by whitespaces.

The problem is: you need syntactically aware editors to work with such a thing. You probably need a fully structural text editor (a semi-structural editor, not unlike Emacs' paredit, may not be enough)...

Because how do you cut&paste between different levels of indentation?

Worse: how do you cut & paste inside an incomplete AST ?

Sometimes it's the programmer who knows what he wants to do, and here he better be helped with structural text editing or it looks like things could quickly go apeshit.

I understand parenthesitis and Lisp-hatred is high on HN but you need to know that a lot of people who do not hate parentheses are using things such a semi-structural text editing (which, amongst other, allows to automagically match parentheses).

So it's not by saying: "zomg, parenthesis, I hate these. Look here ma', no parentheses... But you'll have one heck of a time aligning code blocks" that you're going to convince Lisp users...


It's not a hatred of parentheses. I use Elisp and Common Lisp all the time, I'm fine with parentheses.

If you read the article it's specifically about having liberated macros, something you can't have in the presence of parens. You can't write these macros in Lisp:

  -- Hello,World! =)
  print : Hello, World! :-)
  let x = 23/6²*
          z/+84
That's "--" and ":" and "=". Those are macros.

Cutting and pasting is easy:

If I copy

  foo bar mu
      zot bob   <-- these two
          bil   <-- lines
and then I got to here

  bar zot bob
      dod rob
          |    <-- with my cursor here and paste
what do you expect to happen?

Whereas here

  bar zot bob
      dod rob
      |    <-- with my cursor here and paste
what do you expect to happen?

This is trivial to do in Emacs.


so it's Python but functional?


It takes whitespace to the extreme, too.

I like everything but the "statements span multiple lines" instead of using comma seperated values. I think CSV syntax is clearer and more concise than having newlines act a seperators like that, and it is easier to read.

I like high information density languages with minimum glyphic overhead. That means python is close to the top, but I also consider C++ competitive because it has a lot of expressionism in its verbosity. I also rank YAML high because it has barely any glyphic overhead but still conveys high information in a frame.

It is why I use K&R braces, because I use indentation to deliminate scopes, and having extra semicolons on lines by themselves breaks my information density and drives me nuts, because I like having a consistent per-line flow (I don't mind blank lines though, because I just treat them as null, but one character lines require processing, just not enough to warrant them worth existing in my crazy head).


Isn't that a very shallow parallel?


No, it's Haskell, but strict and with dynamic typing.

So it's actually closer to Lisp.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: