
A tiny language called Z - randallsquared
http://chrisdone.com/z/
======
olympus
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.

~~~
emmelaich
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.

------
agentultra
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.

~~~
raverbashing
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 * .

~~~
RodgerTheGreat
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.

------
evincarofautumn
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.

~~~
chrisdone
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.

~~~
evincarofautumn
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.

~~~
chrisdone
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.

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

------
theaeolist
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.

~~~
quarterto
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

~~~
chrisdone

      k g h
          i j
    

Presumably?

------
Tloewald
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 :-)

------
andrewflnr
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.

------
edtechdev
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...](https://github.com/jashkenas/coffee-
script/wiki/List-of-languages-that-compile-to-JS)

~~~
jberryman
FYI, chrisdone is the author of Fay.

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

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

------
saurik
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>

------
lmm
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.

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

~~~
rm445
No, they are Z-expressions.

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

~~~
skrebbel
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".

~~~
marshray
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.

------
michaelsbradley
Reminds me a little bit of Tcl.

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

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

------
SilasX
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.

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

~~~
akkartik
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>)

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

------
akkartik
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 ...

------
martinced
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...

~~~
chrisdone
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.

------
ronreiter
so it's Python but functional?

~~~
zanny
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).

