6 ms·
Show HN: Indentation-based syntax for Clojure
- agigao 2y agoBut, where are my parens? :D Joking :) Looks rather interesting, to be honest. I'll play with it.
- qwerty456127 2y agoI just want a programming language (probably a Lisp) where every thing is on a separate line, every sub-thing is indented and there is no choice (there always is only one right way to write something). E.g. println(greetings(users)) should always be println greetings users
- jen-ya 2y agoI started my toy language with this idea, but it quickly becomes unreadable, so I chose to support both notations. https://github.com/jen-Ya/tab https://github.com/jen-Ya/tab Here is a Fibonacci example: fn fibo x if (< x 2) 1 + fibo (- x 1) fibo (- x 2
- Vampiero 2y agoThat's XML without the angle brackets
- barrenko 2y agoMaybe try something like Gleam?
- rtpg 2y agoI have more or less exactly this syntax written in a notebook as a transformation. The one critique I'd have here is I think that name binding should be handled differently and _not_ introduce extra indentation. Naming shouldn't create indentation IMO, and it's one of the cardinal sources of readability issues in lisps. But for you you could just avoid it entirely (though you might need to be a bit clever about scoping if you care about lifetimes). Just let me write `a = thingy` and then go to my next line!
- vim-guru 2y agoThe parenthesis are a strength that allows for faster editing and easier parsing. Although I respect every en-devour to have fun with code, I hope people don't run with this as a serious option. It would be better to have editor extensions that emulate this with regular Clojure code; that! I could get behind.
- abyssin 2y agoAt a glance, it looks quite similar to Python.
- jacknews 2y ago[flagged]
- deleted 2y ago[deleted]
- pieterbreed 2y agoWhat the author did is cool in a social sense; lispers go on and on about homoiconicity, which here means that the original lisp code is data. The author uses this property by reading this code/data and transforming it into text, that represents syntax-full code. It is "cool" because it demonstrates the value of homoiconicity. However; it's not cool in a technical sense. The result is not homoiconic any more. Code that had a cool property has been turned into code that does not, and the result looks like every other syntax-full blub language. The result destroys value.
- benj111 2y ago(foo (bar (baz 1))) is equivalent to foo bar baz 1 Its still homoiconic they can still, and in the case of the linked project, probably are turned into the exact same data structure. I get your objection insofar as whats the point? Youre transforming a language that does a thing well into a language that does a thing less well than another language. But on the other hand different approaches make things easier to reason about. sometimes the shell approach of baz 1 | bar | foo may make more sense. sometimes it might be a map, sometimes a for loop. So in that sense, a DSL for python type, scripting makes sense.
- pieterbreed 2y agoI'm sorry if I touched you on an ouchie. > Its still homoiconic > they can still, and in the case of the linked project, probably are turned into the exact same data structure. What you are saying means it is not homoiconic any longer. If you print out the parsed representation of lisp code, you get back the same lisp code. If you print out the parsed representation of this whitespace code, you get back the lisp code (data) also. But this is different to what you fed into the parser. The point of homoiconicity is that the data structure representing the code is the code itself. There's no syntax, it just represents itself. It's super simple. A programming language with syntax cannot be homoiconic. This is complexity. > I get your objection insofar as whats the point? "Easy" IS an argument to choose something over something else, but "simple" is a better, stronger argument. Parens syntax is simple. Whitespace syntax is easy, iff you come from/is familiar with a python-style background. We are debating some obscure thing. The point is the argument :) I hope you have a great day.
- adz5a 2y agoInteresting project, showing how "easy" it is to host[1] another language within Clojure. Like others, I admit I see little value for myself or as a selling point for beginners. For experienced engineers however, like I wrote above it should serve as a case study into how to hook up everything together to produce a working tool. Then it is a matter of seeing if and when there is opportunity to reuse such techniques to build DSLs that compile to native Clojure. [1] Clojure is known to being designed to be hosted within another platform, but like all Lisps it is also a valid (and productive) hosting target by itself. It should be known that many of the APIs in the Clojure ecosystem rely either on: - literal data structures - macros reusing native Clojure patterns / forms such as `let`, `def` ... - the shape of native (ie: defined within clojure.core) Clojure APIs, such as `get-in`, `with`... Some examples: specter, integrant, mount, clojure.core.logic, datalog (datascript, datomic, xtdb), clojure.spec ...
- harperlee 2y agoWouldn’t “let x to 1” be much more clear if it were “let x be 1”? How is this typically read in English mathematical contexts? Or perhaps “as”: with-open rdr to :random-value binding *file* to .getFile(url):
- gsbcbdjfncnjd 2y agoYes. This is a dead giveaway that the author is not a native English speaker. And overall it gives the project a mid vibe.
- benrutter 2y agoThis is really cool, I like it a lot! I don't think the parens in lisp are actually the problem, I think it's the embedding. I.e. List programps naturally tend to get very nested very fast, kinda like this: (- (+ (* 3 4) (- 3 1)) 3) It's just a simple sum, but reading it involves a lot of remembering contexts and how things should work in lisp. The CWP examples uses arrow/pipeline style macros a bunch, which are awesome! I kinda feel like they already solve the issues that a lot of people have with parens: (->> 3 (* 4) (+ (- 3 1) (- 3)) There's still a tonne of parens there, but the flow is a lot more readable for me. Edit: sorry for the very difficult to read code formatting - couldn't figure out how to monospace things in hackernews.
- genezeta 2y ago> Text after a blank line that is indented by two or more spaces is reproduced verbatim. (This is intended for code.) [0] So... (- (+ (* 3 4) (- 3 1)) 3) And... (->> 3 (* 4) (+ (- 3 1) (- 3)) [0] https://news.ycombinator.com/formatdoc https://news.ycombinator.com/formatdoc
- benrutter 2y agoAh nice, thank you!
- geokon 2y agoI often write threaded math but it unfortunately it doesn't compose cleanly with division :) (subtraction can be transformed into an addition without too much extra) If you do thread-last and then need to divide by some value, you will need to write something like (* (/ 1 37)) which is ugly I think you could actually write a fraction here, but if instead of 37 you have a binding then that doesn't work
- Someone 2y agoForth does that without parentheses: 3 4 * 3 1 - + 3 - That’s the ultimate “object verb” way to do it and IMO at least equally readable (ignoring familiarity, of course)
- cies 2y agoI wonder how this works with https://github.com/typedclojure/typedclojure https://github.com/typedclojure/typedclojure
- saurik 2y agoI found--when working in Clojure for a while back in 2012--that what I really wanted was just a way to collapse deeply-nested parenthetical definitions, and ended up with a simple reader macro that reads one form and then pushes the rest of the current form down into the end of that one (and I added a special case for a let). (use 'reader-macros.core) (defn macro-read-one [reader] (clojure.lang.LispReader/read reader true nil true)) (defn macro-read-rest [stop reader] (if (skip-whitespace reader) (let [data (.read reader)] (.unread reader data) (condp = (char data) \; (do (macro-read-comment reader (.read reader)) (macro-read-rest stop reader)) stop () (cons (macro-read-one reader) (macro-read-rest stop reader)))))) (set-dispatch-macro-character \> (fn [reader quote] (let [data (.read reader)] (.unread reader data) (if (= \( (char data)) (concat (macro-read-one reader) (macro-read-rest \) reader)) `(let [ ~(macro-read-one reader) ~(macro-read-one reader) ] ~@(macro-read-rest \) reader)))))) I just find that the GC (and being hosted in Java, in particular) causes you to have to have a very large number of (with-open)s around in just about everything you do to deal with the lack of deterministic finalization, and, when combined with the number of recursive let bindings I was accumulating, I felt like I was going insane. (defn handle [manager socket] #>(with-open [socket socket]) #>(with-open [input (new java.io.BufferedInputStream (.getInputStream socket))]) #>(with-open [output (.getOutputStream socket)]) #>input (atom input) #>output (atom output) #>(let [sendln (fn [& data] #>(let [data (apply str data)]) (println "< " data) #>(let [data (.getBytes data "ISO-8859-1")]) (.write @output data) (.write @output crlf) )]) ...etc etc... ) I am now looking back at some of this code--after not seeing it in a decade--and honestly I'm still very happy with this design choice and still feel like this drastically helps my ability to understand what is going on in some of the more complicated functions, as my level of indentation/nesting is so much lower. So like, if you are going to create a new syntax for Clojure--especially one that is going to force indentation for nesting that could have previously been elided by stylistic choices--my maybe-I'm-unique suggestion would be to add a similar feature that let's you have a version of ":" that just dominates until the end of scope and let's you reduce one level of indent. (Which, I guess, put like that, is a feature I could see also adding to Python. The existence of mutable data--and how the most popular implementation has deterministic finalization via reference counting--means I don't feel quite as visceral of a need for this, but it would still be pleasant at times.) (Ugh... reading that macro reminds me just how angry I found myself getting every day I used that language how ',' was bound to something entirely useless and so they had to break the beautiful symmetry of ',' with '`' in macro definitions. It is something so small, and yet, and I appreciate this is nigh unto ridiculous for someone who clearly didn't exactly like Lisp either enough to not implement this insane reader macro ;P, kept making me sad about Clojure any time I had to touch a macro, and macros were the only reason I was using it in the first place.)
- yogthos 2y agoI'd argue that the main thing this illustrates is how silly the argument that s-exps are hard to read is. A very slight syntactic change makes Clojure look just like Python. Anybody who claims they just can't wrap their head around writing Lisp is not being serious.
- pfdietz 2y agoYes, exactly.
- dismalaf 2y agoIt's a cool thought experiment but you can also use use an editor tool that matches braces and colours them lightly to achieve nearly the same effect, but also more readable.
- rbanffy 2y agoThere is an enormous difference between the things you can do and the things you should do. This one doesn't belong in the second set.
- cylinder714 2y agoThis reminds me of Breck Yunits and his Parsers idea for a programming language: https://scroll.pub/blog/teddyTalk.html https://scroll.pub/blog/teddyTalk.html Scroll, a publishing system, seems to be the main focus of his efforts: https://scroll.pub/ https://scroll.pub/ He's on HN, but his posts and comments irritate people: https://news.ycombinator.com/user?id=breck https://news.ycombinator.com/user?id=breck
- fulafel 2y agoReminds me of Apple's Dylan.