This is maybe the best introductions to Lisp i have seen, especially for a js dev like me it could hardly get more approachable and convincing.
I could not help but thinking at the end, this is really awesome but s-expressions are kind of hard to read and reason about for my brain, it would be cool if we could generate them from more readable syntax, maybe something like javascript :D . Unfortunately the standard js AST seems to be not a great fit but i found a S-Expression encoder for js AST https://github.com/anko/eslisp
This way instead of having to write s-expressions manually we took a whole circle and are back at js but with macros, not relying on eval and having a code is data representation...
> s-expressions are kind of hard to read and reason about for my brain
I think it's likely this is just a matter of familiarity.
Like the way that people think the Windows (/ whatever) GUI is intuitive - but when you test this by putting someone who has never used it in front of a screen, it actually isn't. "What do you mean if I write something and don't also 'save' it, whatever that is, it will disappear? This is supposed to be better than paper...".
The text editor handles indenting and matching. After a while it is completely transparent.
Maybe the last sentence needs a bit expanding, I think most readers already use a powerful text editor and don't see how standard features would help with lisp. The thing is, you don't really edit Lisp as text. Two commonly used extensions (for emacs, vim, vscode, sublime...) are paredit and rainbow. Those extensions kick in when lisp code is detected.
It then feels more like tree manipulation than like writing code. Braces are always matched, you literally cannot delete the closing brace using normal text editing. Each tree depth as its own brace color so it's easy to distinguish between them.
Restructuring tree to join two blocks of code or to extract block to higher level is exposed as command mapped to keyboard shortcut. It's not semantic editing in sense that extension is aware what is function and what is data, you can still make nonsense code. Still, reading and manipulating code is quite effortless.
That becomes a no true Scotsman fallacy though. People argue "Either you like lisp syntax or you haven't used it enough to have a valid opinion". That is a bullshit argument.
Obviously opinions on the language shouldn't be given too much weight if a person hasn't spent too much time investigating/ using it.
As a non-JavaScript person, all the JavaScript examples looks like gobbledygook unless I sit down and consciously think about what the examples are showing, and it kinda hurts my head a bit, but I can slowly force myself through it.
But as someone with a few years of lisp under my belt, even the closure examples (and I've never used clojure) parse pretty close the speed of thought.
It really is hard to explain to someone who hasn't used lisp for 6-12 months, but there really is a lisp "enlightenment" experience, which is totally unlike anything I've experienced with any of my other languages. I'm not saying this makes it better or worse than other languages, but I am saying it's real, and that lisp code is not only parsable, but exceptionally so to an experienced lisp programmer. One day you're sitting there thinking there's a whole lot of brackets, balancing and indentation in this godforsaken language, and then something just clicks over in your brain and you don't even see the brackets any more and it's all just data structure, and you see the shape of it, and you say to yourself "well that was pretty fucking cool" and from that day on you don't really see the brackets any more, it's all just names, indentation and data structures.
edit: and it should also be recognised that an editor implementing parentheses balancing, indentation options, and keyword lookup/ completion also becomes a relatively trivial exercise when your code consists of variable names and structured data.
> and then something just clicks over in your brain and you don't even see the brackets any more
In my experience, the Lisp enlightenment comes in stages, two big ones are 1) s-expression parsing you describe here, and 2) grokking "code is data".
WRT. s-expressions enlightenment, I vividly remember when it happened to me. Few months into learning Lisp, I bought a hardcover SICP and, for some reason, decided to do exercises in it on paper. Few exercises in, as I was writing Lisp code with a pen in hand, suddenly something clicked in my head - like my brain JIT-compiled a new processing module - and from then on, I no longer needed to mentally count or track the parens. I just knew how many and where they were, it dropped to semiconscious level. Ever since, I'm very comfortable with s-expressions; in fact, I prefer them as a notation to Algol/C-like code.
I’ve literally seen first-hand GJS lose track of paren matching while lecturing on Sceme/SICP. It happens very rarely sure, but certainly not never. Probably about as often as experts in any other language/profession make silly mistakes. Perhaps you’re a better schemster than him, but I have my doubts :)
I think no matter the language the syntax will disappear over time and your brain will learn to look at the character matrix and directly see logical constructs, moreover this will always apply cross-language to other languages with similar syntax. Fit example, I primarily work in TS, I have decent amount of experience in other C-style, and to me Rust is easily readable with very little experience actually using it.
I'm not sure who GJS is but if you see any lisper editing text instead of operating on structures (with auto-balancing parenthesis and so on), it's 99% certain it's in an environment they are not familiar with, so they will make mistakes.
I don't think anyone who write Lisp-like languages professionally doesn't use tools like parinfer/paraedit, where balancing parenthesis is not something you have to do.
Yup, makes sense that you make mistakes then, as these folks are surely used to not having to think about balancing parenthesis anymore. Just as over-reliance on GPS will make your navigation skills without a GPS worse over time.
While specifically parinfer and/or paraedit might not be available in neither LispWorks or ACL editors, they surely have something there to aid with parenthesis.
But I might be wrong here, it's simply hard for me to imagine someone writing with lisps professionally going the route of textual editing when structural editing is right there.
I think it's like a language family thing. If your familiar with a C-language (or more accurately, AGOL style) then looking at other C-languages isn't as alien to you. While looking at a LISP family language, it's a different language branch and so you have to learn some new ways of thinking, which can be disorienting.
Ex french & english vs french & indonesian. English and french can recognize words from each other and kind of see some similar grammar rules (the medicine vs. la médecine) while with indonesian those moments are not as frequent.
> This is maybe the best introductions to Lisp i have seen, especially for a js dev like me it could hardly get more approachable and convincing.
The classical one is "The Nature of Lisp"[0], which introduces data-as-code through XML and Java build tools. Same idea, just with examples more relevant at the time of writing. Still worth a read for non-webdev programmers.
Having learned Lisp, it's half funny, half disheartening to watch the industry repeatedly tries to rediscover "code as data", as people's configuration or data files in markup language du jour grow in complexity and eventually start directly encoding executable code... and then stop shy of embracing the code/data duality.
I think that should be the marketing of Lisp: A necessary evil ;)
But seriously speaking, for me Lisp would be much more appealing if it had been introduced to me as a specialized niche language, though for an important niche nonetheless, instead of as the be-all-and-all language for your superpowered startup [1].
(For those that don't already have a goto list of counterarguments to "Lisp all the things": My main contra-point to Lisp is that with all the meta programming powers you get, you write yourself into your own little corner where no-one but you and your friends live. You want advanced syntax highlighting, linting, automatic refactoring for your special features? Write it yourself! You want outsiders to participate (think: new employees)? Write all documentation yourself, too!)
Having worked in a Common Lisp startup, I'll only agree with half of your counterarguments :).
> You want advanced syntax highlighting, linting, automatic refactoring for your special features? Write it yourself!
That's true to an extent. Simple things are simpler in Lisps, because the syntax is trivial. So highlighting and structured editing are easy. The rest, is near impossible, at least for a full-featured Lisp like Common Lisp. That's a consequence of being an extremely dynamic language, that ships a compiler and intertwines parsing, compiling and execution. The flip side of being able to run arbitrary computation at compile time is that, in general, you can't know what the program will do until you run it. The dominant free CL IDE, SLIME, does just that: it queries your running Lisp image for its current state, to provide you with formatting and autocomplete and other hints.
That said, "near impossible" is a function of community size. Had CL anywhere near as much popularity as Java or Python does, I'm sure folks at IDEA would make automatic refactoring work for CL as well :).
> You want outsiders to participate (think: new employees)? Write all documentation yourself, too!
That I strongly object to. Code generation and compile-time execution aren't magic, or even particularly hard concept. They're just another flavor of code. You manage it the same way as regular code - you package it into modules with well-defined interfaces, and document them. I've worked with CL in a team settings, and I've worked on legacy CL code that's as old as I am; it's not harder than "regular" legacy code. And you always have to write your documentation yourself, there's no escape from that, no matter what language you use.
>in general, you can't know what the program will do until you run it
It needs to be added that in Common Lisp, once the program is running you can do everything with it while it's running: Inspect the stack frames, change variable values, rewrite/recomiple/update functions, update class definitions, update objects to said new class definitions, save the current program state to disk,etc.
So it's programming that is geared to RUNNING the program and modifying it while its running.
Seems you're arguing for using all the powers a programming language gives you, nothing in particular about Lisp there. As with many things, it depends mostly on the people writing the code and their process, rather than what programming language they are using.
I never inherited a Clojure-codebase that has been written during long duration, but I have digged around in a fair amount of Clojure-codebases that are open source, both user focused and libraries. Same with JavaScript. And I can say, as someone with more JS experience than Clojure, that the Clojure projects tend to be a lot easier to understand than the JS ones, probably not because of the language, but because of the habits that the language "forces" you into.
I'm mostly arguing against extensive use of meta programming, which is promoted as one of the main reasons to view data as code, as meta programming is inherently disadvantaged for automated tooling. Formulated as a trade-off: When I have to choose, I prefer richness of IDE (and other tooling) automation to program creation automation that I write myself (i.e. meta programming).
This trade-off is not restricted to Lisp, but also applies to e.g. C++ template meta programming. Compilers have gotten betters with templates, but still debugging the more advanced usages of templates can become hellish. Codifying the features the meta programming supplies in the language itself or in well supported libraries means that error messages get better and many usage scenarios are documented on Stackoverflow.
I don't doubt your experience regarding Clojure vs JavaScript code bases, but this probably has to do with other reasons than the meta programming the original blog post is about?
> I'm mostly arguing against extensive use of meta programming, which is promoted as one of the main reasons to view data as code, as meta programming is inherently disadvantaged for automated tooling.
Which is weird, because in most Lisps meta programming happens at compile time. It should be possible for tooling to just show you the expansion of any given macro invocation. In fact, when I used Clojure a decade ago, there was an Emacs command that would expand the macro invocation under your cursor, so you could see what code was actually being generated.
So I don't really think that's a fundamental limitation. I suspect it's more related to the fact that Lisps aren't especially popular, and don't get the attention from tooling that other languages do.
> Codifying the features the meta programming supplies in the language itself or in well supported libraries
But without the meta programming, those libraries might not actually be possible to write. You will either end up with a more dynamic interface (doing meta-stuff at runtime), or a clunkier and more verbose interface. I think being able to expand a macro invocation to see what it turns into is enough for all but the hairiest of macros.
> It should be possible for tooling to just show you the expansion of any given macro invocation. In fact, when I used Clojure a decade ago, there was an Emacs command that would expand the macro invocation under your cursor, so you could see what code was actually being generated.
Exactly. On common lisp, for example, it's just a keypress, and it has a "macro stepper" so it shows the first expansion possible, then the second expansion, and so on and so on... until you end up with compiler primitives!
> You want outsiders to participate (think: new employees)? Write all documentation yourself, too!)
Shouldn't you be doing this anyway? Not trying to be snarky: it's that every job I've worked at has a classic underdocumentation problem. Tribal knowledge dominates, and the practicalities of shipping product overrun the need for teaching employees, new and old, about the idiosyncrasies of the system. [edit] This seems hardly an issue with LISPs or DSLs in particular.
> My main contra-point to Lisp is that with all the meta programming powers you get, you write yourself into your own little corner where no-one but you and your friends live. You want advanced syntax highlighting, linting, automatic refactoring for your special features? Write it yourself!
This is contrary to my experience (though I've mostly used Racket rather than Common Lisp). Tracking what's a function vs macro, where an identifier is introduced/used, etc., is all baked into the underlying language and not something I have to write myself for every language extension I make.
> You want outsiders to participate (think: new employees)? Write all documentation yourself, too!
Were you planning to not document your internal-use utility code?
"Any sufficiently complicated C or Fortran program contains an ad hoc, informally-specified, bug-ridden, slow implementation of half of Common Lisp."
Also:
> Hacker Robert Morris later declared a corollary, which clarifies the set of "sufficiently complicated" programs to which the rule applies "...including Common Lisp."
>Hacker Robert Morris later declared a corollary, which clarifies the set of "sufficiently complicated" programs to which the rule applies "...including Common Lisp."
This is referring to CL implementations that depend heavily on C.
Many CL implementations now are almost 100% Lisp code.
JavaScript isn't based on Scheme, it's what would have been Scheme if deadlines and marketing needs didn't make Netscape use a hacky toy language instead.
The place where JavaScript contains "an ad hoc, informally-specified, bug-ridden, slow implementation of half of Common Lisp" is Babel.
If you take anything away from that: brackets are the most compact way to serialize a tree, for some reason they are called s-expressions. The parse tree for lisp is the syntax tree, you just rename each bracket node to whatever the first atom in the bracket happens to be.
One thing that XML could have done that s-expressions can't is a way to serialize a dag if proper nesting of tags wasn't required. I'm playing around with a language based on that, the mental load of having no clear way to delineate blocks visually makes it hard to reason about, unfortunately.
> I could not help but thinking at the end, this is really awesome but s-expressions are kind of hard to read and reason about for my brain, it would be cool if we could generate them from more readable syntax, maybe something like javascript :D .
So many people start out thinking like this when they learn Clojure or some other Lisp and then after like 2-4 weeks of reading Lisp code, any C-like code starts to look comparatively irregular and Lisp feels much easier to read.
I heard that before but i wonder how much is survivorship bias :D it reads just so awkward to me as a programming language, even though i 100% dig the beauty and everything that it allows.
It's just a different paradigm than what most developers are familiar with so there is a learning bump, same as functional programming if all you've done is object-oriented stuff. The awkwardness is only temporary. If you can already see the beauty of Lisp code, then you just need to build a little project in it to make the awkwardness fade away.
I recommend Clojure/ClojureScript. It's quite practical for web development and also an excellent gateway into functional programming.
I used Common Lisp as my main language for about 6 years (long time ago now) - yes for the first few days the syntax is a bit jarring but I've found that any new language is fundamentally different in approach to what you are used to has that effect (pretty much the same happened the first time I saw C, Lisp, PostScript etc.)
Here is one anecdata. When my daughter was 14 I taught her a Lisp and then a traditional programming language. She found the random syntax of the trad formatted language bizarre.
I used Java for 10+ years, including now, and I worked briefly in Clojure for less than 1 year. I think the Clojure syntax is way more reasonable, although I appreciate the maturity of tools used with Java.
Two things were important to make it "click" for me:
1) I realized that "Lisp has many parentheses" actually has it backwards. A Lisp code has about as many parentheses as an equivalent Java code; there is almost a 1:1 relation. It just has less of... unnecessary things. Which results in more "parentheses per square meter", but from this perspective that is a good thing!
2) Some people complain that dense code is more difficult to read. But you don't have to turn everything into one-liners! You can format it into just as many lines as the Java code would have, except the lines will be much shorter now, and therefore easier to read. In Java, you don't have much freedom with formatting, because a typical statement takes more that half of the screen width, so you are stuck with one long column. Lisp with shorter statements gives you more freedom. You can abuse it to write hard-to-read code. You can also use it to design beautiful code that is easy to read. Also, there is a huge difference in legibility of a code that fits in one screen (so you can see it whole at once) and one that does not; and the Lisp functions are shorter on average.
I can only speak about Clojure, since it’s the only lisp I know. After just a few days of playing with it though, I started to see the parentheses as a warm and friendly hug, wrapping everything inside of them in its own scope that doesn’t affect its parents. Reading JavaScript, my main driver, became harder in comparison.
function doSomething(a, b) {/* something! */}
is okay, since the “function” keyword is a simple indicator, and it’s clear that it’s a function declaration. The kids these days often use
const doSomething = (a, b) => {/* whatever */}
and that is awful for readability. It’s much harder to scan for function definitions amidst value declarations. I’m nearly 20 chars in before I even know it’s a function. Worse yet is something like
const doSomething = (a, b) => b => “some closure”;
This threatens to stretch my capability to understand the context I’m in when reading it. “b” is just kinda floating there amongst the infixed arrows, and I have to read on to know it’s an argument, and I have to run through the calculation of what “b” is every time. Compare:
(defn do-something [a b] (fn [b] “some clojure”))
I can count the parens, there is no implicit scoping of the function body. My editor can too, which means I often don’t have to. It’s obvious that it returns a function. This part is Clojure-specific, but I also have strong guarantees that the returned function cannot pull the rug out from under me with whatever I pass to it by arbitrarily mutating some argument.
To me at least, (fn-name args) was both more readable and makes more sense than fnName(args) after a few days, and I never learned a lisp until I was 37. Maybe I just found a style preference later in life, marking me as a lisp survivor. Maybe we’ve just been doing it wrong for decades and have grown accustomed to it. I can’t say. But the power of the language itself coupled with the code editing features it enables makes me think that the whole field has been on the wrong track, or maybe even off the rails, for decades.
The JS code I write now is more flexible, more resilient, more testable, and more maintainable as a result of learning Clojure. I doubt anyone can say the opposite: that a C-style language improved their understanding of lisp. (Not addressing you with this part, dear poster, since your gripe is rooted in the syntax.)
[NaN] “kids these days“ is just friendly ribbing. I’ve got the grey beard now, so I‘m free to play the part. Language maintainers tend to be older, and we cause more problems.
Everything is so extremely context sensitive and has much more focus on text over symbols than other languages, which isn't a problem for computers but is a huge problem for humans. Therefore lisp is objectively harder to read for humans. If you have never read other code and is super familiar with lisp it is easier to read, but anyone with experience with both will find other languages much much much easier to read thanks to the much better UX design.
I don't see how you can argue otherwise. It is as if you have no clue at all about how UX works. You can't get used to everything, there is a hierarchy of syntaxes, and lisp syntax is just plain bad for humans.
I have experience with tons of languages, lisps and non-lisps.
I don't really get this argument. It's not any harder to read `(if foo` than it is to read `if (foo)`. It's purely a matter of what you've gotten used. I don't think it's an objective measure, it's purely about what you've already trained your brain to pattern-match on.
> and has much more focus on text over symbols than other languages
Funny how you get the exact opposite complaint about languages like Perl.
Let’s talk about text over symbols and readability.
Return the maximum value of the array “numbers”. The languages are listed in descending order of text > symbol.
Clojure:
(apply max numbers)
JavaScript:
Math.max(...numbers);
APL (I couldn't find how to apply this to an arbitrary array):
⌈/ 4 3 2 7 5 1 3
Here’s a guess:
⌈ numbers
The most aymbol-focused is the least readable to me, and Clojure is the only one that returned good results from a Google search to figure out what was going on. Both “JavaScript ...” and “APL ⌈” gave me irrelevant results.
Furthermore, how is this
(if a
true-cond
false-cond)
harder to read than this, aside from familiarity?
if (a) {
trueCond
} else {
falseCond
}
In the latter case, the if/else requires a special syntactical structure. And since it cannot be used as an expression, the common desire to conditionally assign a result spawned the ternary operator: a whole separate syntax that only applies when you want to assign a result to something. In lisps, you just use “if” wherever you need a binary conditional.
While I agree with your statement, I would probably downvote your comment if it wasn't grey already. The reason is that you mostly state your opinion (aggressively so), but fall short on arguing why your opinion is true. If you can extend your argument / extend the list of arguments, I think your comment would find more appreciation.
People saying that Lisp is as easy to read since you get used to it is like people saying that salad is as tasty as cake since you get used to it. Sure someone eating cake for the first time after having only eaten non sugary stuff for a long time will find it a bit jarring, but anyone eating both will find cake so much tastier, which is why people overeat cake so much.
Yet many people will still adamantly say that they find salad tastier. And still go on and eat cake and get fat. They are just lying to themselves, I'm not sure why. Lispers are the same way, its just a reality distortion field.
Considering that we're familiar with the latter syntax (more or less) since kindergarten, I'd say... yes. And the idea that you'd somehow start to consider something you've been doing all your life as “irregular” after 2–4 weeks of Lisp cure is just hilarious.
Depending on how frequent are complex equations in your code, you could make a macro for it, and write it e.g. like this:
(math 5 * f (x + 2))
I might be tempted to do so if I frequently encountered math expressions of depth 3 or more, which in my short Lisp career I didn't.
Also, I would probably rewrite the expression as:
(* 5 (f (+ x 2)))
to make it easier to read by putting the simple operand first; or in case of two difficult operands I would use formatting:
(* (f (+ x 2))
(g (+ x 3)))
Note that a good editor would check that the parentheses match the formatting, so I wouldn't really count that 3 closing parentheses are needed in the last line; they would be inserted automatically.
>And the idea that you'd somehow start to consider something you've been doing all your life as “irregular” after 2–4 weeks of Lisp cure is just hilarious.
And yet we learn all kinds of syntax that we haven't been familiar at all (zero indexing, x=x+1, etc which we used to the exact inverse: math assignments being immutable or denoting an equation not increment, etc),
-- not to mention advanced stuff like generics, futures, closures, etc --
Yes. It's all personal, of course, but I find that the typographical variety of "traditional" languages with syntax makes it way easier to me to read/parse them than LISP. Parens, parens, everywhere, nor a drop of structure.
And when it comes to macros and (pseudo)-quoting, the LISP is hands down more obnoxious (again, for me) than, say, Python's f"{}".
> It's all personal, of course, but I find that the typographical variety of "traditional" languages with syntax makes it way easier to me to read/parse them than LISP.
I wonder how much of that has to do with your familiarity with "traditional" language syntax. For example, when I started writing Lisp, I had a similar opinion. But I write Clojure professionally for a while, and that disappeared. Now I haven't written Lisp in probably 7 years, but I still have no problem reading it.
> Parens, parens, everywhere, nor a drop of structure.
On the other hand, in some sense it's all structure. There are certainly advantages to having everything be delimited.
Yes, it's just that it's the useless (unless you write an AST-rewriting macro) structure that gets in my way. Say, naming a thing, a function definition, and a function invocation are all very visually different in, say, JS, while in LISP it's just a slightly different pattern of parens and two keywords ("let" and "lambda") that are, of course, not actually keywords but just happen to be interpreted in that way by the eval.
And yes, human ability to pattern-match things is astonishing, I am sure if I were to program exclusively in Scheme for half a year, I too would one day grow accustomed and used to it. But do I want to? I am really not convinced about that. A human can get used to pretty much anything, even to almost constantly being in mild pain, but... no. I'd rather just not.
> it's the useless (unless you write an AST-rewriting macro) structure
In a good JS editor, how many key combinations and mouse clicks are required to jumping into, jumping out of, and cutting a block of code (eg. a function definition or a conditional expression), or transposing, merging, splitting, annexing, and de-annexing 2 blocks of codes? It usually takes me at most 2 key combinations with a Lisp editor (including navigating the cursor to the right place), thanks to Lisp's uniform structure.
> Say, naming a thing, a function definition, and a function invocation are all very visually different in, say, JS
Aren't these also visually highlighted in a Lisp editor as well?
Besides, a Lisp editor can optionally blur the parentheses so users don't mentally have to.
(edit: formatting, recounting the key presses required)
> I believe it's also 1 or 2 shortcuts, thanks to the IDE's understanding of the language syntax, unless I misunderstood your scenario?
I have not seen transposing, merging, splitting, annexing (moving a block into the inside another block), and de-annexing (the opposite of annexing) 2 blocks of codes in JS without using the mouse yet, so I just want to check.
> Besides, another languages can throw away the parentheses entirely
JS uses parentheses for grouping complex arithmetics and for function's argument lists (eg. func(arg) in JS vs. (func arg) in lisp). JS also uses curly braces to mark code blocks, which is similar to Lisp parentheses but at the cost of more complex parsing for the compiler and the mental distinguishing between functions' argument lists and code blocks on the coder (they are just lists).
JS statements use semi-colons, which makes editing them feel like editing lines of codes while editing Lisp statements is editing nodes of a tree, which is a very different experience.
> Clojure professionally for a while, and that disappeared. Now I haven't written Lisp in probably 7 years, but I still have no problem reading it.
Slightly off-topic but interesting none-the-less. After starting to program with Clojure both as an hobby and professionally, how do you go back something that is not lisp/repl driven?
I've tried time and time again to go back to JavaScript, as I used to be OK with it, but I just cannot justify the hassles I have to fight with everyday, compared to if my co-workers just picked up Clojure instead.
Going back to something that's not REPL-driven takes some adjustment, but I try to find and focus on the advantages the other language has, instead of on what it's missing. For example, when using OCaml (which incidentally does have a REPL, but I don't use it often), I get a lot of value from the type system and the module system.
Even Java has advantages in terms of tooling, and from Java 8 on, you can write code using some Clojurish idioms with streams. Though for immutable data, you need something like Lombok + pcollections.
JavaScript, on the other hand, really doesn't offer anything over Clojure, so I can see why you'd struggle going back to it. It's just a downgrade.
> it would be cool if we could generate them from more readable syntax
There are a few options out there; maybe take a look at Sweet Expressions? Some Lisp implementations have native support for different syntax, but the nice thing about these formats (which are essentially just serialisations of concrete syntax trees) is that we can convert them into the expected format automatically, e.g. using a pre-processor.
It has nice, readable, ruby-like syntax, but it's a Lisp at the core. Really powerful macros (see Ecto), and a syntax that isn't off-putting to the majority of programmers.
Most things in Elixir, including constructions like if and def, operators and even module accesses (`Module.function`) are compiled to a simple AST based on function calls.
Because that AST isn't usually written by humans, besides the function and the arguments, each node might contain additional context information, like the file and line number the call appeared in. That lets you do some really cool stuff, like distinguishing x (a variable you define) from x (defined by a macro), which is a frequent source of bugs in other languages that support macros.
99% of Elixir is syntax sugar over function/macro calls, and its true AST is smaller than Clojures.
> I could not help but thinking at the end, this is really awesome but s-expressions are kind of hard to read and reason about for my brain, it would be cool if we could generate them from more readable syntax, maybe something like javascript :D
Little known fact: The people who came up with lisp wanted to do this, too.
I think the story continues to where, in the beginning, they thought a change would be good, but after seeing the benefits that s-expressions gave, changed their minds.
Lisp has only one rule called the Operational Form:
(operator arg1 arg2 arg3 ...)
The Operational Form is just a list denoted by parentheses. The operation or 'function' comes first, followed by its arguments or operands, e.g.
(+ 1 2 3)
=> 6
The + symbol resolves to the plus function and is passed in the arguments 1, 2 and 3. Nested forms are evaluated inside-out:
(+ 5 (- 10 6))
=> 9
Traditionally, Lisp only has one data literal: `(linked lists)`, but Clojure adds `[square brackets for vectors]` and `{:curly brackets}` for hash maps. The colon denotes a keyword, which is a symbol which only ever resolves to itself and is commonly used for labeling things, e.g. keys in a map.
Notice how the operational form is just a list with some symbols and data literals. In Lisp, the syntax for writing data structures and the syntax for writing code is the same syntax. And since a function operates on data and produces new data, what if a function could operate on code and produce new code, since code is just data?
We call such a function a macro (and I don't mean Excel macros). Macros run at "compile-time" (technically 'read time') and the code output is executed at "run-time" (or during 'evaluation'). The benefit of macros is that if your language is missing a feature, you can add it.
You can see this in practice by looking at the source code for the `and` and `or` logic functions in Clojure, which are typically built-ins, but in Clojure they are just macros bootstrapped on top of the special forms `if` and `let`: https://github.com/clojure/clojure/blob/38bafca9e76cd6625d8d...
Clojure only has 13 special forms:
[fn let let loop do while . if def recur
try catch throw quote var
monitor-enter monitor-exit]
Everything else is built on top of that.
When I started learning Clojure, I found the ClojureScript Koans to be very helpful in getting a feel for the semantics and to become familiar with the argument placement: http://clojurescriptkoans.com/
If you come from a traditional OO-background, my condolensces and I recommend starting with Rich Hickey's 2-hour talk, "Clojure for Java Programmers": https://www.youtube.com/watch?v=P76Vbsk_3J0
I could not help but thinking at the end, this is really awesome but s-expressions are kind of hard to read and reason about for my brain, it would be cool if we could generate them from more readable syntax, maybe something like javascript :D . Unfortunately the standard js AST seems to be not a great fit but i found a S-Expression encoder for js AST https://github.com/anko/eslisp
This way instead of having to write s-expressions manually we took a whole circle and are back at js but with macros, not relying on eval and having a code is data representation...