"Apple plans to require that all application software for MacOS be approved by Apple first."
I'm ready to leave MacOS if this becomes reality (no way to install apps outside of Apple Store). So so far it's pretty easy to install custom apps and grand them root permissions.
There's no "if", notarization has already happened. As of a few months ago, macOS Catalina warns users if an app hasn't been notarized.
It's the latest step in a multi-year effort to make macOS a little less of a wild-west of software, but as with all the previous steps, it's pretty trivial for knowledgeable users to override - you just right click on an app and choose "Open" and you can bypass the signature/notarization checks.
I would like Crystal to include some modern features instead just being a fast and nice language. How about stuff like: immutability, good functional programming support, complex union types, compile-time guarantees?
Check out Crystal’s official website sometime to learn more about its modern features. Crystal doesn’t have immutability, but does have support for functional programming and union types. “Compile-time guarantees” is such a vaguely specified feature that virtually all programming languages have that, but its type system is actually pretty strong despite how it looks with all the type inference, and even prevents null pointer exceptions unlike a lot of static languages.
It also has a lightweight threading system that makes it work well as a web server. Not quite as elegant as something like Elixir, with its isolated processes, but still nice. It also supports macros, which can make the code a lot more elegant and performant compared to similar languages.
Are you going to add some features for more structured data? I'm thinking about tables like in Notion where you can sort by any column. Working with Markdown tables is very clunky.
The closest thing in Markdown is probably front-matters. You're right in that Obsidian is not great for structured data right now, and for that purpose Notion or Airtable is much better.
If you need to edit tables then you could just use TableFlip, gives you a spreadsheet style interface to edit tables, automatically updates your markdown file without the need to copy paste https://tableflipapp.com
The author usually gets <5% share from the book price. I guess it's much better (although not legal) to just skip buying the book and send a donation directly to the author.
> I have to admit, that at the beginning using brackets was not easy for me. Once I’ve realized that the brackets are just on the other side of the function name, everything was simple and I could code very fast.
A question to fellow Lispers: is there a good reason why LISPs do not put parenthesis they way we know from math? Why `(f foo bar)` and not `f(foo bar)`?
> is there a good reason why LISPs do not put parenthesis they way we know from math? Why `(f foo bar)` and not `f(foo bar)`?
Because the first is a list and the second isn't. The whole point of LISP is to represent code as data, and the fundamental data structure it uses is a list.
> What prevents someone from adding syntactic sugar f (x y) for (f x y)?
Because (cons 1 (cons 2 (cons 3 nil))) is a list and f (x y) isn't. You could put parens around the latter to make it (f (x y)), but then you might as well save a pair of parens and just write (f x y).
Depending on what `a` is, it certainly could mean something. Those are two expressions, where `f` by itself does nothing and, if `a` is a function, it will be applied to `b`. If `a` is a macro it will be transformed, and then depends on what it will be transformed into. If `a` is neither, then it's an error.
For fuck's sake, these things don't mean anything by themselves. The only reason they mean different things is because the syntax is defined this way. So back to the original question, why is the syntax defined this way?
Hmm... I see. (x y) is a list, g (x y) is a function call, f (...) is another function call, so f (g (x y)) is (f (g x y)) in regular LISP syntax. The syntax is parsed from innermost to outermost, so where is the ambiguity?
(x y) is also a function call, because lisp always interprets the first position of a list as such unless you quote it:
f (g ‘(x y))
But that changes the meaning of g from “a fn with two args” to “a fn with one arg that is a list.”
If your meaning is instead that function-outside notation cannot be mixed with s-exps, I.e. you have to choose one mode or the other, then I think you’re right that there’s no ambiguity here (and no need to quote the args above). I suspect you would lose the power of macros, and may not be able to use existing ones, but I don’t write them so I’m not sure.
Below are some Clojure expressions with standard and then function-outside versions. I quickly found that I wanted to go back to attached() parens() for those, because that seems more readable to my eyes than having () them () spaced out. I’d never choose this option though, it took all of an afternoon to get used to lisp syntax, and I find it to be more readable now than c-style languages.
(let [a 1 b 2] (+ a b))
let ([a 1 b 2] + (a b))
let( [a 1 b 2] +(a b))
(apply + (range 10))
apply(+ range(10))
(defn my-fn [x] (* x x))
defn (my-fn [x] *(x x))
Because Lisp has two syntax levels: one of s-expressions and one for the programming language on top of s-expressions.
S-expression examples
(berlin madrid london)
(john (age 21) (weight 65))
Lisp code examples:
(if (> a b) a b)
Non Lisp code, but a valid S-expression:
(if (> a b) then a else b)
You can define a new syntax for Lisp - like it has been done before - where you traditional syntax to define a programming language. You then just need a grammar and a parser.
Originally s-expressions were only thought for data and programs would have a different syntax.
A conditional might be written like that.
cond a > b -> a ;
t -> b
Lisp code with lists might then look like:
append[ (a,b,c) , car[list] ]
Where (a,b,c) is a literal list of three symbols.
But the Lisp system was internally defined using lists and the compiler&interpreter were using Lisp code as lists.
Thus it was seemed more practical to just work with s-expressions (nested lists, ...) and defer the question of syntax to a later time.
Later multiple attempts had been made to define a Lisp with a more traditional syntax: the Lisp 2 effort in the 60s, various syntactic front ends, RLISP, LOGO, ML, Dylan, ... . But for Lisp programmers it was more practical to keep the s-expression syntax, because the internal machineries of Lisp are using s-expressions anyway
We're talking about syntax, not abstractions. There's no reason why f(x y) can't be a representation of a list that is represented by (f x y) in most Lisps.
No it isn't; and your definition is infinitely regressive because it further implies that (cons 1 (cons 2 ...)) is syntactic sugar for (cons 'cons 1 (cons 'cons 2 ...)) and so on.
I find this statement from Paul Graham's Revenge of the Nerds enlightening:
> Lisp looks strange not so much because it has a strange syntax as because it has no syntax; you express programs directly in the parse trees that get built behind the scenes when other languages are parsed, and these trees are made of lists, which are Lisp data structures.
In my (very limited) experience, the benefit's not from representing functions this way, it's from representing everything this way -- including things that would be special syntax in other languages. It makes it easier to write and debug things like macros if the input is already represented as essentially an AST and the output doesn't need to be converted back into special syntax before it can be run.
If you wrote function applications as `f(foo bar)`, then spaces between identifiers and parentheses would be semantically-significant. Putting a space between `f` and the first parenthesis in the aforementioned expression would produce an invalid expression. Similarly, doing the same with `g(baz f(foo bar))` would result in `g(baz f (foo bar))` which is an application of g to 3 arguments, second of which is the procedure bound to f, whereas in the original expression it was an application of g to 2 arguments, second of which was the result of application of f to two arguments. I don't think such sensitivity to whitespace would result in comfortable code editing when you move code around.
Contrary to many of the responses you've got, the real answer is that it's just a convention with no particular reason other than historical cultural preference. For evidence, I present R: it looks like C syntactically, but all constructs actually desugar into function calls (even assignments!). And function calls themselves are also C-style - f(x, y) - but once it's parsed, the result is a "pairlist", which is exactly what it sounds like to any Lisper. So any R program is represented as nested lists, with constants and symbols as leaf nodes, exactly as in Lisp.
'pdonis gave you the correct answer, but another way of thinking about it: because Lisp way is more consistent. It also makes it more powerful.
The math notation we commonly use is mostly a legacy notation, invented before we had computers and could do (or conceive of) symbolic processing. We write `f(foo, bar)` instead of `(f foo bar)` not because the former is better, but because it's been established before we invented trees (data structures) and figured out why they're important.
It's great, but the differences between runtimes can be painful. In practice you still need your project to be aware of which Clojure code files are platform-specific and which are safe to run across all platforms.
I'm ready to leave MacOS if this becomes reality (no way to install apps outside of Apple Store). So so far it's pretty easy to install custom apps and grand them root permissions.