A tiny language called Z
chrisdone.com
chrisdone.com
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 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.
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.
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 * .
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. 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. 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.
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.(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.
k g h
i k
k g
h k g h
i j
Presumably? k g h
i j
→
k (g h
i j)
→
k (g h
(i j))
In Lisp syntax: (k (g h (i j))No regrets :-)
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...
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.
No-one has a problem with the S programming language co-existing with S-expressions.
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".
(I have an interest in this space: http://github.com/akkartik/wart#readme)
-- lorem ipsum
-- quidquid dior
defun ...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...
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.
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).
So it's actually closer to Lisp.