[HN Gopher] We need to talk about parentheses (2020)
___________________________________________________________________
We need to talk about parentheses (2020)
Author : precompute
Score : 75 points
Date : 2024-02-12 07:16 UTC (15 hours ago)
(HTM) web link (andreyor.st)
(TXT) w3m dump (andreyor.st)
| boxed wrote:
| > It depends... on operator precedence
|
| I think implementing operator precedence in ANY programming
| language is a mistake. `1 + 1 * 2` should be a syntax error. In
| C, in Python, in Java.
| tempodox wrote:
| I vigorously disagree.
| lionkor wrote:
| For that example, I agree, but how about a
| | b || c >> d & e * f
|
| ? I think its reasonable to say that a language should not
| allow that
| OJFord wrote:
| You can take any reasonable grammar and come up with
| unreasonably ugly/illegible code.
| lionkor wrote:
| Yeah, of course, but grammar can be designed as to not
| allow as much implicit magic.
|
| If you can do it, someone will do it (in prod, in your
| product).
| tempodox wrote:
| The reason why this expression shouldn't compile has
| nothing to do with precedences (C has some odd choices here
| to be sure), but because of mixing incompatible types.
| Arithmetic integers, bitvector integers, Booleans, and
| possibly even floats. Conversions to appropriate types
| should have to be explicit. But C's type system is weak as
| all hell and this shit actually compiles there. (I'm only
| mentioning C because your example looks like it.)
| rfl890 wrote:
| There's no implicit type conversion here. As long as all
| the vars are the same type it works (albeit unpredictable
| if you don't know operator precedence for the lang).
| bmacho wrote:
| How about *paste Linux source code here*? Should the
| language C allow it? It's long and complicated, you don't
| even have enough time to read it, let alone to comprehend
| it.
| samatman wrote:
| That expression would not pass code review around me, and a
| linter should flag it for sure.
|
| But all the precedence rules you're invoking here are
| useful in isolation, and I see no reason why they shouldn't
| parse in combination. A linter can use heuristics to flag
| cases which should be parenthesized, a parser would need a
| defensible rule for failing a parse. What would that rule
| be?
| sokoloff wrote:
| There is value in notational simplicity.
|
| For the same reason that math has defined precedence (and even
| omits multiplication signs entirely in many places),
| programming languages in some domains will choose to do the
| same.
|
| It's not a problem if you want to create a language that does
| not do this, but I think it is a problem if you want to ban
| others from doing it.
| tsimionescu wrote:
| Algebra only really has defined precedence between
| multiplication and addition/subtraction. In all other cases,
| even division, there is either a non-linear textual
| representation, or parantheses are required. You have
| fractions, exponents, radical signs, various signs which
| appear only at the beginning of an expression (sigma,
| integral, lim) etc.
|
| Even multiplication is often distinguished more by the shape
| of the text than by actual precedence rules, once you leave
| basic arithmetic - expressions like "1 - 3xy + 2y2" make the
| order of operations obvious from text layout, which is why
| they are preferred over "1 - 3 x x x y + 2 x y ^ 2", which no
| mathematician ever writes.
| sokoloff wrote:
| Exponentiation also has defined precedence, does it not?
| (Everyone who has passed algebra agrees that y is squared
| before being multiplied by 2 in your example.)
| tsimionescu wrote:
| You can view that as precedence, but I think it's more
| correct to say that exponentiation binds to the single
| symbol it is attached to. When you write x2 + 2, I don't
| think it's operator precedence that makes it clear this
| is not equal to x ^ 4.
|
| Similarly, in the following expression: x
| _ + 2 2
|
| I don't think it's precedence that makes it clear you
| first divide by 2 and then add 2 to the result.
| sokoloff wrote:
| Alternatively, perhaps what you're observing is simply
| the graphical representation of the underlying
| mathematical precedence rules. (Obviously, over hundreds
| of years of symbolic math, the notation and precedence
| has co-evolved, with precedence probably generally, well,
| preceding notation.) y = x / 3 + 2
| y = 2 + x / 3 y = x/3 + 2
|
| We all agree those are the same, despite a lack of an any
| cues in the first two. That suggests to me that there is
| a defined precedence for division vs addition in math.
| (So much so that it doesn't even seem like a
| controversial statement to make.)
| tsimionescu wrote:
| Precedence rules always apply to a specific textual
| notation. There is no precedence between division and
| addition, there is a precedence between the / and the +
| operators.
|
| People are generally bad at remembering arbitrary
| operator precedence rules. That is why most math notation
| doesn't rely on precedence rules to be unambiguous, it
| uses other kinds of textual clues. For example, the /
| operator is virtually never used in algebra. Instead,
| division is indicated by fractions, which explicitly
| group their operands and separate them from other
| adjacent operations. Exponentiation relies on super
| scripts to separate its arguments, making it extremely
| unambiguous at least which operations are part of the
| exponent and which the total. Roots use the radical
| symbol which extends over the entire expression you're
| taking the root of, and again rely on a kind of
| superscript for indicating which root you are taking.
| Limits and sums use subscripts to separate the iteration
| direction from the expression. Integrals use both sub and
| superscripts.
| sokoloff wrote:
| > Exponentiation relies on super scripts to separate its
| arguments, making it extremely unambiguous at least which
| operations are part of the exponent and which the total.
|
| The textual representation even for exponents is not
| enough if you don't already know that exponentiation has
| higher precedence than multiplication.
|
| "1 - 3xy + 2xy2"
|
| Both of us know that only the y term is squared in that
| expression. How do we know that?
|
| I claim that we know that because we know the precedence
| rules and that someone never exposed to our system of
| algebra would not inherently and unambiguously know that
| it was 2*x*(y2) rather than (2*x*y)2 or even 2*(x*y)2
| merely by visual inspection of the expression.
|
| This (the meaning of the above expression, including the
| precedence/grouping) is something that has to be taught
| in Alegbra/Prealgebra courses, and is something that some
| students struggle with.
| tsimionescu wrote:
| For expnentiation I was talking about something else:
| 1+3 x + 4
|
| In this case, the exponent notation makes it unambiguous
| that this is (x^(1+3))+4, and not x^(1+3+4).
|
| I agree that xy2 could mean both (xy)2 and x(y2), and
| that perhaps you need to understand precedence to
| disbiguate. I would still contend that the notation is
| defined as "each term has its own exponent, possibly 1
| which need not be written" to disambiguate, where terms
| are separated by parentheses or by other operators.
|
| That is, in my mind, the grammar for usual algebraic
| notation is something like this:
| expression = term (op term)*
| expression___________ (expression)? term = (num |
| var | fraction | \( expression \) | \/expression)
| op = + | - | / | x | deg etc
|
| And the rules are that you first evaluate each term, and
| then the operators between the terms. You only need to
| follow precedence rules for those operators between
| terms, the evaluation of a term is unambiguous.
| samatman wrote:
| You appear to be inventing elaborate ways to talk about
| precedence without saying "precedence".
|
| Precedence, under the term "order of operations",
| predates computers by several centuries. Which is not to
| apply that it was fixed in stone from the beginning, or
| perfectly consistent, but mathematics agreed on the
| modern rules universally in the first couple decades of
| the 20th century.
| thaumasiotes wrote:
| > Alternatively, perhaps what you're observing is simply
| the graphical representation of the underlying
| mathematical precedence rules.
|
| This is nonsense. There are no underlying mathematical
| precedence rules. You're seeing precedence being
| represented visually, but there is no mathematical
| difference between 5 3 ADD 2 MUL and 5 3 2 MUL ADD.
|
| MUL and ADD are just binary operators, or if you prefer,
| functions from U x U to U for some set U.
|
| Your statement is _particularly_ nonsensical as applied
| to the example you 're supposedly responding to. Given
| some expressions: 5 + 3 5
| 5 ------- ; --- + 3 ; ------- 2 2
| 2 + 3
|
| How do you even form the argument that the difference in
| interpretation is "simply the graphical interpretation of
| the underlying precedence rules"? The precedence rules
| here are obvious, because they don't exist:
|
| - Division occurs between the upper operand and the lower
| operand
|
| - Addition occurs between the left operand and the right
| operand
|
| It's impossible for these to conflict, so there is no
| such concept as precedence.
|
| (Precedence also doesn't exist between addition and
| addition because addition is associative. It is necessary
| between division and division, and the system is
| straightforward: lower precedence is indicated by longer
| lines. Tell me about the underlying mathematical
| precedence rule that determines that.)
| sokoloff wrote:
| > There are no underlying mathematical precedence rules.
|
| You might want to correct the information here then:
|
| https://en.wikipedia.org/wiki/Order_of_operations#Convent
| ion...
| thaumasiotes wrote:
| What do you think I'd want to correct? That page says the
| same thing you've already been told twice: there is no
| underlying mathematics, and the meaning of the notation
| is defined by the notation. Here's a quote:
|
| > These rules are meaningful only when the usual notation
| (called infix notation) is used. When functional or
| Polish notation are used for all operations, the order of
| operations results from the notation itself.
|
| You could "correct" that to observe that "the order of
| operations results from the notation itself" is true of
| all notation, including infix notation, but why? It's not
| wrong now.
|
| Can you really not tell the difference between "notation"
| and "math"?
| samatman wrote:
| "Order of operations" is the _order_ in which operations
| _proceed_. Hence _precedence_.
|
| They aren't a fact about mathematics. They're a fact
| about _notation_. Operator precedence and order of
| operations are synonymous.
| lex-lightning wrote:
| Yes, but programming is more akin to copywriting than
| mathematical notation most of the time.
|
| Math has very well-defined, well-trod conventions that are
| _densely_ packed into the notation. When people try to write
| code like this though, it's hard to read.
|
| All else equal, I'll take a snippet of code with more
| characters in it but less work to unpack those characters.
| Reading linearly is cheap; I get tired reading formulas
| faster than reading technical prose. Precedence makes the
| mental parsing nonlinear.
| dheera wrote:
| The REAL problem comes from math and physics teachers (even
| at the university level) writing things like:
|
| (a + b) / 2p
|
| instead of
|
| (a + b) / (2p)
|
| It seems that math teachers put division above multiplication
| in order of operations but programming languages almost
| universally treat division and multiplication as equal and
| read them left-to-right. I've fixed multiple bugs before that
| were due to this.
| lifthrasiir wrote:
| Some operator precedences are way more reasonable than others,
| and those between arithmetic operators are good examples. What
| we actually need is a partial order of operator precedences:
| you can have `1 * 2 + 3` or `full_name ?? first_name ++ " " ++
| last_name` but not necessarily `1 * 2 & 3` because that
| combination of operators is uncommon and a parenthesis should
| be preferred instead of an arbitrary total order.
| zarzavat wrote:
| Should `x == 1 or y == 3` be a syntax error? Because that's
| operator precedence too.
| kragen wrote:
| that's certainly a plausible position; in lisp (or = x 1 = y
| 3) is generally an error; even if it's not an error it
| doesn't mean what you intend it to. having to write (x == 1)
| || (y == 3), similar analogous to the code you'd write in
| lisp, would be only a minor burden
|
| in c, x == 1 || y == 3 works fine, but unfortunately the
| bitwise operators have the wrong precedence; x & 2 == 2 is
| parsed as x & (2 == 2), which is never what you meant. so
| even the best software designers have made terrible blunders
| in precedence design
|
| math formulas are a bigger issue; 3*x**2 + x/2 + 1 is a lot
| better than the fully parenthesized version
| zarzavat wrote:
| There are of course operations with unclear precedence
| relationships and it's completely reasonable to want to
| force parentheses when composing those operations.
|
| However, there are also operations with crystal clear
| precedence relationships, such as logical conjunction and
| comparison, and in that case it has to be asked what
| exactly would the parentheses be there for?
|
| In lisp the answer is: to enable syntactic homogeneity and
| to enable macros.
|
| But in other languages like Python that don't have sexpr
| based syntax, parenthesizing everything would just be
| pointlessly unreadable.
| ctrw wrote:
| That is type inference actually.
| frud wrote:
| For basically forever compilers and languages have been
| designed with a total ordering on operator precedences. The
| Yacc compiler generator tool maps operator precedence to
| integers, as do most handwritten compiler parsers.
|
| A total ordering has a definite answer to the question "is x
| greater than, equal to, or less than y?" for all x and y. A
| partial ordering will answer "I don't know" for some x and y.
|
| It's quite possible to implement operator precedence using a
| partial ordering. When the parser has to resolve precedence
| between two operators and their relationship is not defined,
| throw an "ambiguous precedence" error.
|
| You can implement the partial ordering by putting all the
| operators into a DAG of sets of operators. If two operators are
| in the same set, they have equal precedence. If there is a path
| through the DAG from one operator to the other, that defines
| the precedence relation. Else there is no relation.
|
| Say "*" is defined to have a higher precedence than "+", and
| they both have an undefined precedence relation with "&". Then
| "1 + 2 * 3" should compile into "1 + (2*3)", but "1 + 2 & 3"
| should throw an "ambiguous precedence" syntax error.
| adrian_b wrote:
| The lack of operator precedence has no relationship with the
| mandatory use of parentheses.
|
| For example the APL syntax for expressions, which I prefer, is
| that there is no operator precedence.
|
| The operands are associated to operators strictly from the
| right to the left, regardless which operators or functions are
| used, which is a much wiser choice than the reverse traditional
| order of evaluation.
|
| No parentheses are used, except when they are needed to modify
| the default evaluation order.
| samatman wrote:
| That would get really annoying.
|
| Not in expressions like 1 + 3 * 6. Personally, I include the
| parentheses anyway, as a matter of style, and would write 1 +
| (3 * 6).
|
| But in expressions like i + 3 > 4 / n? No thank you, (i + 3) >
| (4 / n) would drive me up a wall. Ymmv.
| phoe-krk wrote:
| _> humans can't immediately parse structure with a glance, and
| sometimes we have to count parentheses by hand._
|
| This is why automatic indentation is important when working with
| Lisp. This is mentioned later in the article, but I consider it
| _mandatory_ because of the multitude of problem this approach
| resolves.
|
| For example, upon pasting the below snippet into emacs:
| (let* ((foo 10) (bar (+ foo 32)) (* foo
| bar))
|
| The editor 1) notifies me that there is a missing paren
| somewhere, 2) automatically* reindents this code into:
| (let* ((foo 10) (bar (+ foo 32)) (*
| foo bar))
|
| At this moment, it's obvious that we have three variable
| bindings, one of them malformed, and the programmer can correct
| this by planting a missing paren in the proper place. I wrote
| about this topic at length in
| https://mov.im/blog/phoe%40movim.eu/cd3577f6-fb1d-45f5-b881-...
| before.
|
| * with aggressive-indent and electric-indent enabled in emacs, as
| soon as the missing paren is placed at the end
| elwell wrote:
| And for that added flair, don't forget:
| https://www.emacswiki.org/emacs/RainbowDelimiters.
| bloopernova wrote:
| Aren't there 11 parentheses in both of your code snippets? I
| must be missing something, sorry could you possibly explain
| where the missing parenthesis should be?
| taeric wrote:
| It helps to basically vocalize what the code is doing. It is
| a "let" form, which has a list of variables, and then a body
| section using them. (It is a "let*", which also means that
| the variables can refer to variables that come before them,
| but not too relevant here.)
|
| To that end, the first () in a "let" should be of the forms
| (name value), where value can be a longer form, if needed.
|
| To that end, the last (name value) in the list should almost
| always look like (name value)), as it is closing the first in
| the list, that is ((name value).
| lelandbatey wrote:
| They're not saying that the second snippet is "fixed",
| they're saying that the auto-indented second snippet
| highlights the mistake to the reader. Yes there are 11
| parentheses in both the code snippets because both are
| "incorrect" in that they're both missing a parenthesis. The
| GP is trying to point out that since Emacs automatically re-
| indents the first snippet so that it looks like the second
| snippet, the reader knows immediately that something "isn't
| quite right".
| bloopernova wrote:
| Ahhh, understood. Thank you!
| phoe-krk wrote:
| (let* ((foo 10) (bar (+ foo 32))) (*
| foo bar))
|
| Or, to demonstrate the ugly "nailclip" style:
| (let* ((foo 10) (bar (+ foo 32))) (*
| foo bar) )
|
| Note that I moved a closing paren off the third line and onto
| the second one, to close the variable bindings in the LET*
| form.
| bloopernova wrote:
| That explains it, thank you!
| adrian_b wrote:
| Automatic indentation helps, but the main problem is that LISP
| uses a single kind of parentheses in all places where some kind
| of brackets is required.
|
| If different kinds of brackets had been used for every distinct
| purpose, then there would have been no need to count
| parentheses or be dependent on enforcing invariable indentation
| rules.
|
| ALGOL 68 is another example of programming language with fully
| parenthesized non-ambiguous syntax, but it uses many kinds of
| different bracket pairs, which makes it much easier to read.
| While some of its bracket pairs, like if/fi, do/od, case/esac,
| have been rightfully mocked, they achieve well their purpose.
|
| In a programming language there are many different purposes for
| which bracket pairs are needed, e.g. for delimiting sub-
| expressions in order to change the default order of evaluation,
| for delimiting function argument lists, for delimiting array
| index lists, for delimiting blocks, for delimiting function
| bodies, for delimiting record/structure component definitions,
| for delimiting the components of various program structures,
| like conditional expressions and loops, and so on.
|
| For an automatic parser, the use of different kinds of brackets
| can only make its task easier, never harder, because in the
| worst case its lexical analysis could replace all kinds of
| brackets with a single kind, while for a human reader different
| kinds of brackets are always helpful.
|
| Using many kinds of bracket pairs is not necessarily as verbose
| as in Algol 68, because Unicode offers enough bracket pairs to
| choose for all purposes.
|
| In LISP all parentheses look the same, but a LISP programmer
| who does not recognize that the things delimited in those
| parentheses are of different natures, e.g. "(CAR ..." vs.
| "(COND ..." vs. "(QUOTE ..." and so on, cannot write high
| quality LISP programs.
|
| Many years ago, I have written many rather big Scheme or LISP
| programs, because they have many advantages beyond the uniform
| parentheses. If I would use again a language of the LISP
| family, I would use a text preprocessor to pass the source to
| the compiler, which would allow me to use various kinds of
| bracket pairs, for instance Unicode mathematical angle brackets
| for COND, Unicode bag delimiters for loops, curly brackets for
| blocks (curly bracket is the Unicode term for braces), and so
| on.
| alexisread wrote:
| I've often wondered if it was worth a language having
| interchangeable brackets that you could use similar to
| 4-colour problems eg. ([1 2] 3) is the same as {[1 2] 3} or
| ((1 2) 3) semantically, just visually easier to read.
|
| Any takers?
| adrian_b wrote:
| This is more or less like the traditional use of
| parentheses, square brackets and curly brackets in English
| texts, where they had the same meaning but they were used
| for different levels of nested brackets.
|
| IIRC, I have seen once an experimental language which
| worked exactly like in your proposal, but it was long ago
| and I do not remember which was it.
|
| One LISP system that I have used long ago had a different
| approach. In it, one closing square bracket could be used
| to close all non-closed parentheses preceding it.
| IanCal wrote:
| We have ides with different colours for different layers of
| brackets, and we have them with rendering different
| characters (>= etc).
|
| So it feels like this could be a reasonable plugin?
| Gibbon1 wrote:
| You could have three slightly different brackets, one for
| each level of nesting. And when you get to four that's it.
| More than three is illegal.
|
| Edit: Or you could use markup to make the text size smaller
| and smaller.
| mikhayahu wrote:
| Racket works this way.
| jacknews wrote:
| Yes, this is my argument against significant-whitespace
| languages like python, or even worse for simple nested data
| like yaml.
|
| Actual delimiters explicitly structure your code, and then
| automatic formatting confirms it, kind of like double-entry
| book-keeping.
|
| For me, just a couple of days with lisp and parentheses made me
| wish every language had such simple explicit syntax, I really
| recoil at some language's special syntax, eg python list
| comprehension. Maybe I just hate python, lol.
| somewhereoutth wrote:
| ~ let* ~ ~ foo 10 ~ bar (+ foo 32)
| ~ * foo bar
| tromp wrote:
| > Here's the same code, but with all parentheses in place:
| (define (fac x) (if (<= x 0) 1 (* x
| fac (- x 1))))
|
| Still one pair missing.
|
| Is it true that in normally formatted LISP code, every line
| should start with an opening parenthesis?
|
| Btw, I prefer Haskell's syntax of fac x = if x
| <= 0 then 1 else x * fac (x-1)
| mtreis86 wrote:
| Nope, lisp doesn't care about whitespace at all, including line
| endings. The way lisp is read in is as if there was no
| whitespace, just objects in lists. (foo bar
| baz) (foo bar baz) (foo bar
| baz)
|
| All the same thing to lisp
| tsimionescu wrote:
| That's not quite right. Lisps care a lot about the _presence_
| of whitespace, just not about their type or number. (foo bar
| baz) is very very different from (foobarbaz). They are
| identical in this respect to the C family of languages, in
| fact (the only difference is that they allow newlines in
| quoted strings, which C languages normally do not).
| samatman wrote:
| The term of art is _whitespace insensitive_ , meaning that
| whitespace is only necessary when needed to separate
| tokens, and any amount will do when it's allowed. While it
| may be possible to write a language in which whitespace may
| be inserted or left out literally anywhere, I'm unaware of
| any actual languages where this is true.
| KingOfCoders wrote:
| Coming from Java, Scala, Javascript one nice benefit of Go, was
| no parantheses around if conditions. At first I thought an
| irrelevant change but now I do think it makes code more readable.
| xmcqdpt2 wrote:
| Probably one of the best thing about the new "pythonized" Scala
| 3 syntax is that it doesn't require parentheses around if
| conditions.
| wlindley wrote:
| >>> ... "curly braces" ... << Ugh. Braces _are_ "curly" -- there
| are no "square braces" or "round braces." Needlessly redundant
| and confusion-inducing. Can we please eradicate this
| circumlocution from programming language?
| _Wintermute wrote:
| This is a regional thing. In the UK they're all brackets.
|
| Brackets, square brackets, curly brackets.
| tsimionescu wrote:
| They are also called curly brackets, so I imagine the confusion
| comes from there, and from the fact that the words "braces" and
| "brackets" are relatively similar, making them easy to confuse,
| especially for non-natives.
| ehecatl42 wrote:
| Parentheses (()), braces({}), brackets([]), and chevrons(<>). I
| concur.
| tsimionescu wrote:
| Or, in British English, brackets(()), curly brackets({}),
| square brackets ([]).
| iainmerrick wrote:
| And I've heard both "angle brackets" and "pointy brackets"
| (<>)
| lex-lightning wrote:
| Smooth, squiggly, hard, and pointy are my gotos
| PeterisP wrote:
| That would be fine if we could somehow magically change the
| English language so that this would universally match how
| people call these symbols around the world - but that's not
| likely to happen.
| teddyh wrote:
| <http://www.catb.org/~esr/jargon/html/A/ASCII.html>
| codingdave wrote:
| Considering the worldwide population of coders don't all share
| a common native language, no - we need to embrace fluidity in
| our vocabulary.
| oakpond wrote:
| Maybe the outcome of this syntax experiment is that less is not
| always more. Maybe in general people prefer more syntactical
| notations than what Lisps offer out of the box.
| zelphirkalt wrote:
| Also many people, who have actually used Lisps prefer the
| notation for its ease of editing code.
| kragen wrote:
| programmers a choosing a language because of syntax are like
| house buyers choosing a house because of the color it's painted
| lex-lightning wrote:
| But what if the two houses are otherwise identical? Even a
| small convenience will tip decisionmaking
| kragen wrote:
| in cases similar to that, it would be a reasonable thing to
| do
| tmtvl wrote:
| I'd like to think I'd have a far easier time painting a mauve
| house ochre than I'd have trying to change Rust to have an
| S-expression syntax (not in the least because I have the
| programming chops of a slightly dimwitted housefly).
| erik_seaberg wrote:
| The wrong paint won't stop you from adding a garage, but
| choosing a complex and non-uniform syntax makes it so hard for
| end users to extend the language that we almost never try.
| Imagine how much more readable Java would be if Lombok didn't
| need to smuggle each option in a prefix @Annotation.
| samatman wrote:
| I seem to recall some fellow or other describing notation as a
| tool of thought.
|
| That doesn't sound superficial to me.
| bmacho wrote:
| I don't think we need to talk about parentheses?
|
| I doubt there is anything nontrivial but useful to say about
| them?
|
| Anyone found anything in the article?
| Joker_vD wrote:
| YALPAAG (Yet Another "LISP's Parentheses Are Actually Great")
| article.
|
| One would think that if they _really_ were that great, there
| wouldn 't have been any need to keep writing such articles after
| several years, and yet here we are again.
|
| > humans can't immediately parse structure with a glance
|
| No, humans absolutely can, as long as those structures are: a)
| delimited; and b) the delimiters used aren't nauseatingly
| repeating. LISP's parentheses start failing at the b) condition
| much sooner than semantic whitespace/indentation starts to.
| nataliste wrote:
| YAEAR (Yet Another "Earth's Actually Round") article.
|
| One would think that if the earth was _really_ round, there
| wouldn 't have been any need to keep writing such articles
| after several years, and yet here we are again.
|
| > humans can't immediately see curvature with a glance
|
| No, humans absolutely can, as long as those curvatures are: a)
| intuitive; and b) the curvatures used aren't nauseatingly
| large. Round Earth start failing at the b) condition much
| sooner than a flat-earth model.
|
| In short, generations are forced to effectively re-learn basic
| facts about the world they inhabit because new members are in
| fact ignorant and must be disabused of their intuitive yet
| incorrect assumptions.
| samatman wrote:
| What on Earth did you intend to convey with this? I'm
| mystified.
|
| Are you, in fact, comparing a preference for Algolic syntax
| over s-exprs, to... flat Earth theory?
|
| Surely not.
| xmcqdpt2 wrote:
| > And this is, in fact, a made-up example but the thing is, I was
| a C programmer for the past 5 years, and I was working with low-
| level code (bare metal actually). And I have never seen the use
| of explicit scoping of variables. [...] I find this a major
| problem when dealing with C because I always see about 10
| uninitialized variables at the start of each function, where half
| of those are used only once. Somewhere. And thank god, if there
| are no global variables.
|
| Sure, if you write C like it's 1995, it's not going to be very
| readable. You don't need to define variable at the beginning of a
| scope, and you can use {} to scope variables to tighter blocks.
| And functions can be small.
|
| (Sometimes you need to keep variables for longer than you would
| like for cleanup reason, that's true.)
| ctrw wrote:
| So your solution to having too many () is to have just as many
| {}. I'm not complaining but...
| PeterisP wrote:
| Well, no, the above solution is to have a {} _if and only if
| explicit scoping is desired_ instead of having just as many
| {} as there would be ().
|
| Sometimes you need explicit scope for a bunch of things and
| the code would benefit from it. That doesn't justify having
| explicit scopes for everything all the time.
| zelphirkalt wrote:
| Isn't is always safer to have variables scoped only to the
| relevant part of the code, that is supposed to make use of
| them? So basically it would result in almost the same
| number of parens.
| lex-lightning wrote:
| This isn't always possible, even in lisp. Compare to
| Rust's non-lexical lifetimes
| zelphirkalt wrote:
| I don't see how it could ever be not possible. Maybe not
| always convenient, but impossible? How?
| lex-lightning wrote:
| a = getInt(); b = getInt() + a; c = getInt(); d = b / c;
| puts(d); puts(c); puts(b); puts(a);
|
| Nothing in the middle of this code prevents using 'a'
| even though it isn't relevant to 'c'. And there's no way
| to arrange it to change that.
| zelphirkalt wrote:
| If `c` does not depend on `a`, then the re-arrangement
| seems simple: Define `c` later.
| susam wrote:
| > If `c` does not depend on `a`, then the re-arrangement
| seems simple: Define `c` later.
|
| How does that prevent 'a' from being in the scope where
| 'c' is defined? It is not clear to me what you mean by
| defining 'c' later. Later where? And how do we ensure
| that 'a' is not accessible there?
|
| I think lex-lightning's comment has a point. We need to
| define 'a' and we need to define 'c' and then we need to
| print 'c' _before_ printing 'a', so although 'c' does
| not depend on 'a', we still need to keep 'a' in scope
| because we cannot print it before we print 'c'.
|
| Perhaps a convoluted solution to the problem posed in
| lex-lightning's comment would be something like this:
| #include <stdio.h> int getint() {
| return 42; } int main() {
| int a = getint(); int b = getint() + a;
| int c; { // Shadow 'a' here
| to make the outer 'a' inaccessible in this scope.
| int *a = NULL; (void) a; c =
| getint(); } int d = b / c;
| printf("%d\n", d); printf("%d\n", c);
| printf("%d\n", b); printf("%d\n", a);
| return 0; }
|
| Output: $ cc -std=c99 -Wall -Wextra
| -pedantic foo.c && ./a.out 2 42 84
| 42
| lex-lightning wrote:
| I appreciate your interest.
|
| I'd argue that even declaring 'c' is against the spirit
| though. To say nothing of the eldritch shadow cast there
| (no disrespect, I think we're both enjoying playing
| around here)
| susam wrote:
| > I'd argue that even declaring 'c' is against the spirit
| though.
|
| Yes, I agree. My code example is meant to show the best
| we can possibly do to solve the problem in your comment
| and how contrived that solution is. The fact that this
| solution is contrived and diverges from the intended
| spirit of the problem only emphasizes the point of your
| comment.
|
| That's why I am eager to understand what zelphirkalt
| really meant when they wrote, "the re-arrangement seems
| simple: define `c` later". It seems far from simple and,
| in fact, impossible if we want to avoid contrived
| solutions.
| zelphirkalt wrote:
| I see now how the problem is. Thanks. I have a suspicion,
| that this depends on side effects and that without
| assignments and without side effects things might look
| different. Somehow I don't seem to come across these
| scenarios, when I write FP programs.
| lex-lightning wrote:
| Hey I'm down to party with some monads, but I leave that
| as an exercise to the reader
| lex-lightning wrote:
| The drawback of the microscopic function craze is you lose the
| ability to read a whole procedure from top to bottom. If some
| code is truly reused, break it out into a function with a good
| name, sure.
|
| But in my domain I have seen countless one-line functions used
| in one place each, and it's frustrating having to jump around
| the code (and add to my mental context stack*) rather than just
| putting some curly braces and a comment. Functions aren't
| primarily meant as a hygiene mechanism. I really like the curly
| brace scoping in Rust.
|
| I found my style partly by reading John Carmack and Casey
| Muratori's coding philosophies and putting them to the test for
| myself.
|
| * For a natural language analogy, consider the difference
| between parenthetical remarks and asterisks. You have to
| remember your place in the text.
| jolt42 wrote:
| I define functions as often in let/letfn blocks than I do as
| separate functions, so they are local and in context. Makes
| it easy to read.
| lex-lightning wrote:
| This sounds like another option while maintaining agreement
| with the gist of what I meant. Those are good, though
| that's where I start preferring smallness
| agentultra wrote:
| It's a personal preference based on how we mentally model
| problems. I cannot stand long procedural code. The mental
| gymnastics required to know what it does is too much. More
| than 5-6 variables in scope and I have no idea if it does
| what you say it does. It would take me some time to figure
| that out. If it is under test we can at least say it's not
| wrong some times.
|
| I much prefer breaking problems down to the level where I can
| reason in terms of sets, algebras, and that sort of thing.
| It's much easier for my brain to rely on properties and the
| ability to substitute terms in equations than it is to
| reconstruct a giant state machine and execute it in my head.
| Much more so when we add concurrency to the mix.
|
| At the end of the day though I don't think there's, "one true
| way."
| lex-lightning wrote:
| When I was a kid I would tell my parents I cleaned my room,
| then they'd look in the closet and see everything in a
| pile.
| pimlottc wrote:
| Don't let perfect be the enemy of good
| nyrikki wrote:
| Mostly a problem with people who borrow their concepts of
| cohesion from MVC, but yes people tend to fundamentally
| misunderstand SIP and it results in over modularity.
|
| IMHO that is a side effect of how we teach people how to
| program mixed in with orgs cargo culting acronyms and not the
| core concepts of ideas.
| lex-lightning wrote:
| For real though. This was my biggest frustration when I was
| an SE.
|
| Want people in a large organization to ignore your ideas?
| Speak them out loud.
|
| Want them to follow them as dogma? Write a blog post under
| a pseudonym.
| mkehrt wrote:
| You should be able to read a function from top to bottom and
| it should show you the whole procedure _at a given level of
| abstraction_. Hiding the details is the heart of what
| programming is.
|
| I always try to write code like
|
| fn foo() { let foo = do_first_thing();
| let bar = do_second_thing(); do_third_thing(foo,
| bar);
|
| }
|
| whenever possible. Then you can see the abstract overview of
| what the code does at a glance, but you can drill down into
| the details if you need to.
| wslh wrote:
| In a simple DSL I am working based on s-expressions I enable
| things like removing parenthesis in single line expressions. For
| example, (define a "hello world!") is just define a "hello
| world!" <EOL>
|
| Another example that could work more than RPN/FORTH (or close to
| the ; operator in smalltalk) is:
|
| (* 4 (+ 1 2 3))
|
| equals to:
|
| + 1 2 3 . <EOL>
|
| * 4 <EOL>
|
| The DSL is not LISP based but s-expr to define a DAG like in an
| spredsheet.
| QuadmasterXLII wrote:
| Pssst- hey kid! I heard you came to this side of town looking for
| a lisp with no parentheses. If you're not a
| narc, I've got the goods. Try a little
| bit of this julia! end
|
| Ok, ok I know your parents warned you about indexing from 1 but
| they also thought that global variables should be replaced with
| singleton factories, so what do they know.
| klabb3 wrote:
| Monster! You'll have to pry my 0-indexing from my cold dead
| hands.
| lispm wrote:
| We actually need to talk about lists. The single reason why Lisp
| uses parentheses is the list notation -> programs are actually
| lists, not just text.
|
| The same code which runs CL-USER 10 > (let*
| ((foo 10) (bar (+ foo 32)))
| (* foo bar)) 420
|
| becomes data, when quoted: CL-USER 11 > (quote
| (let* ((foo 10) (bar (+ foo 32)))
| (* foo bar))) (LET* ((FOO 10) (BAR (+ FOO 32))) (* FOO
| BAR))
|
| A typical Lisp function can then substitute all * symbols to +
| symbols: CL-USER 12 > (subst '+ '* '(LET* ((FOO
| 10) (BAR (+ FOO 32))) (* FOO BAR))) (LET* ((FOO 10) (BAR
| (+ FOO 32))) (+ FOO BAR))
|
| EVAL can execute lists: CL-USER 13 > (eval
| (subst '+ '* '(LET* ((FOO 10) (BAR (+ FOO 32))) (* FOO BAR))))
| 52
|
| Thus the single main feature is that Lisp itself is an
| application of the List Processor. This feature then is _user
| visible_ in read eval print loops, debuggers, inspectors, code
| formatters, source interpreters, code generators (- > macros),
| embedded languages, ... So Lisp developers loved it, because one
| would not need translators between user facing (infix) syntax and
| internal data structures (ASTs, ...). Thus early attempts for
| other syntax variants failed, because this duality between data
| and code has some kind of elegance. Something which for example
| impressed Alan Kay (of OOP / Smalltalk / Dynabook / ... fame),
| when he understood that something like a Lisp evaluator can be
| written in a few lines of Lisp code, processing lists, which is
| code. -> https://www.quora.com/What-did-Alan-Kay-mean-by-Lisp-is-
| the-...
| taeric wrote:
| I had fun trying to explain this a few years back.
| https://taeric.github.io/CodeAsData.html
|
| I do have to confess I have rarely taken huge advantage of this
| fact. But the idea is pretty easy to help look at code.
| kqr wrote:
| List is confusing terminology. It's a tree. Sure, any node of a
| Lisp tree is implemented as a list of branches, but
| fundamentally we are talking about trees, still.
| zelphirkalt wrote:
| Would you like to call them "TREEPs"?
| lispm wrote:
| Foremost, it's just nested lists and not a tree. We are
| talking about lists and atoms. We are then talking about list
| operations, like getting list elements, appending lists,
| reversing lists, listing things, nesting lists and atoms,
| mapping lists, matching lists, destructuring of lists, ...
|
| This is a list: (munich frankfurt dresden)
|
| This is a list of lists ((munchen :state
| Bavaria) (frankfurt :state hessen )
| (dresden :state sachsen))
|
| If we look at the level of conses and atoms, then we see a
| binary tree and operations: car, cdr, cons, ... Lisp is there
| a low-level language: lists are made of simpler building
| blocks: two-item nodes and atoms.
|
| This is a tree: ((a . (10 . nil))
| . ((b . (20 . nil)) . nil))
|
| The dot is an infix syntax element for a cons cell.
|
| Which as a nested list is shorter written as
| ((a 10) (b 20))
| skybrian wrote:
| It's a shallow understanding of the code if you don't know the
| variable definitions and types. Good enough for some things,
| though.
| lispm wrote:
| Correct, that's where for example an interpreter walks the
| code and builds up the understanding. Similar a compiler, a
| code walker or other tools.
|
| The shallow view allows in Lisp to provide new/different
| views of code. That's good and bad. Many transformations of
| code don't need a deep understanding, many do. In Lisp I can
| write (infix 3 + 1 * sin (x)), because an infix macro can
| provide its own interpretation, by transforming the code. But
| then I need to parse the code and find out what the things
| are: variables, literals, operators, calls, ...
|
| For example when I want to substitute FOO with a call:
| CL-USER 34 > (LET* ((FOO 10) (BAR (+ FOO 32)))
| (symbol-macrolet ((foo (print 2)))
| (* foo bar foo bar foo ; 1-3
| (flet ((foo (a) (* foo a))) ; 5
| (foo (* foo bar)))))) ; 4 2 ; 1 2
| ; 2 2 ; 3 2 ; 4 2 ; 5
| 2370816
|
| Here the symbol-macrolet knows which FOO to expand, and which
| not. There are five expansions in the code, which makes 2
| printed five times. You can see that it works in nested code,
| too : it knows that some FOO are function names and those
| will not be expanded.
|
| Whereas a MACROLET knows, where an operator needs to be
| expanded: CL-USER 38 > (LET* ((FOO 10) (BAR
| (+ FOO 32))) (macrolet ((foo (a)
| `(print ,a))) (* foo bar foo bar
| (foo bar) ; 1 (flet ((bar (a)
| (foo a))) ; 2 (bar (* foo
| bar)))))) 42 ; 1 420 ; 2
| 3111696000
| danieltanfh95 wrote:
| The weird thing about parentheses is that lispers only really get
| how powerful and how simple it makes programming after prolonged
| exposure, and thats why every now and then we have articles that
| try to explain this magical moment to folks.
|
| The strength of lisps syntax basically boil down to a few things:
|
| 1. Scope is visually clearer than in non-lisps, you dont need to
| hold the parser in your head while reading code.
|
| 2. Code as lists allows consistent and simple macros.
|
| 3. No need to reinvent editor tooling.
| melagonster wrote:
| This is a interesting idea!
|
| Maybe all programming languages have their sweet points that
| learners find "magic" and then generate blog articles.
| jwindle47 wrote:
| I loved this article, this is exactly what programmers in my
| community complain most about with regard to lisp.
|
| The greatest strengths for me are the scoping and how lisp
| enables interactive development. For one, I don't even see parens
| anymore when writing code. They've become automatic where I'm
| able to parse structure at least better than I have in the past.
| With a REPL, (I specifically use Clojure), development has become
| fun again. A typical debugging process is attaching to my actual
| program, and calling functions seeing what happens.
|
| I love the flow of defining functions in my code, `def`-ing some
| test data, and calling those functions. I'll use `cider-inspect`
| to see if the data structure looks correct and iterate.
|
| Everyone should at least try a lisp. Iterative, interactive
| development is so fun and powerful.
| RodgerTheGreat wrote:
| Programming languages are user interfaces. The syntax of a
| programming language is part of that user interface. Some choices
| are poor by virtually any measure (stropped keywords come to
| mind; as visually hideous as they are unpleasant to type), but
| most choices represent tradeoffs that favor certain usage
| patterns over others.
|
| Lisp de-prioritizes legibility at a glance and concision in order
| to give maximum priority to simplicity in metaprogramming. I'm
| not convinced this is a good tradeoff; I would no sooner choose
| to drive to work in an amphibious tank that got 0.2 miles per
| gallon to be able to cut across a single river along the way.
|
| Lispers say that I could use specialized tooling to compensate
| for poor ergonomics, but in _many_ contexts- someone else 's
| computer, a text box or static text on a website, a general-
| purpose textual diff or search tool, print in a book, a
| whiteboard- these affordances are not available, and I would
| still have to contend with the inconvenient reality of Lisp's
| _very real_ design choices.
| thaumasiotes wrote:
| > stropped keywords come to mind; as visually hideous as they
| are unpleasant to type
|
| It's pretty common today for people to voluntarily put SQL
| keywords in all caps.
|
| > Lisp de-prioritizes [...] concision
|
| Really?
| RodgerTheGreat wrote:
| By the standards of a K programmer, Lisp is _quite_ verbose
| and excessively nested.
| trealira wrote:
| > Really?
|
| Yeah, Lisp can be verbose due to the lack of built-in syntax
| for things other than lists.
|
| Take the Common Lisp syntax/library functions (there's not
| really built-in syntax) for hash tables:
|
| https://cl-cookbook.sourceforge.net/hashes.html
|
| Compare it with Python dictionary syntax:
|
| https://developers.google.com/edu/python/dict-files
|
| Almost everything that's built-in to other languages has to
| be given a name that makes sense in Lisp. It makes it more
| readable in some ways, but also more verbose.
| NikkiA wrote:
| alists and plists are a much closer lisp analog to python's
| dicts though
| trealira wrote:
| That's fair. Dictionary literals aren't really any more
| concise than a-lists.
|
| A-list: (("the" . 50) ("be" . 25))
|
| Dictionary literal: {"the": 50, "be": 25
| }
| RodgerTheGreat wrote:
| Using both a-lists and p-lists as mere structural
| conventions- and thus needing to convert back and forth
| or write multiple versions of routines to handle each- is
| a good example of how sexprs provide a superficial veneer
| of simplicity while inviting cancerous complexity in
| practice.
|
| The Clojure approach of recognizing associative
| structures as a fundamental building-block and giving
| them specialized syntax: {:the 50 :be
| 25}
|
| Is a clear-cut improvement.
| vindarel wrote:
| Please use the newer Cookbook!
| https://lispcookbook.github.io/cl-cookbook/data-
| structures.h...
|
| For a short hash-table notation, I now use Serapeum's dict:
| (dict :a 1 :b 2)
|
| and that's it, you now have a hash-table (with a
| representation than can also be read back in by the lisp
| reader).
| kqr wrote:
| > Lispers say that I could use specialized tooling to
| compensate for poor ergonomics
|
| Well, in input- and tooling constrained environments I have
| preferred Lisp for its syntactic simplicity. I wouldn't call it
| poor ergonomics.
| whartung wrote:
| I find coding in Lisp to be very comfortable. If I ever
| decided to pull the trigger and swap [] for (), it would
| remove a large portion of the "chording" that happens with
| coding. And I by "chording" I refer to the shift key.
|
| I love kebab-case-its-so-easy-to-type. May as well be spaces.
| You can pretty much do everything with 26 lower case
| characters, the -, space bar, and (). What's that, 30
| characters? I have 46 unshifted keys I can use, plus the
| spacebar. So thats 16 more keys for the more common symbols.
| Just gotta remap the keyboard a bit.
|
| Recall early Lisp actually used things like (plus ...) and
| (minus ...).
|
| Not have to chord is a real benefit, to me, when it comes to
| typing. Lisp just flows that way. For me my-class I find a
| lot easier than either MyClass, or my_class.
| KingMob wrote:
| > Lisp de-prioritizes legibility at a glance and concision in
| order to give maximum priority to simplicity in
| metaprogramming.
|
| I'd need to see a source for this claim to believe that S-exprs
| are primarily for the benefit of metaprogramming. They have a
| lot of ancillary benefits in terms of uniformity, simplicity,
| and scope. Plus, learning paredit gives a lot of power at the
| keyboard, and you can't build an editing setup without
| something like S-exprs (or angle bracket equivalents like
| XML/HTML).
| mindslight wrote:
| I've got this theory that what people actually don't like about
| Lisp parentheses is not the amount of nesting, nor the opening
| paren's prefix location, but rather that parentheses always
| include a hard nesting apply (or really, the syntax layer
| equivalent of a hard nesting list). In other words - most
| programmers are used to having an assertion that ((foo)) (foo)
| and foo are all equivalent, and also assume the basic top-level
| production is _already some kind of list_ (not a single
| expression). Lisp breaks these long-loved assumptions.
|
| Writing Haskell feels quite Lispy in that the basic syntax is
| quite generic, but you can nest parentheses all day long without
| changing the meaning, or even use some generic operators ($, .)
| to write out a structure in a clearer way. I'd love to see an
| attempt at a Lisp syntax that felt similarly. (And yes, I know
| there have been 2^37 attempts at different Lisp syntaxes, but I
| still haven't found one in line with this idea)
| Beijinger wrote:
| This is why every sane person uses an RPN calculator. :-0
| norir wrote:
| This comes off more as a series of rationalizations than a
| compelling argument. Lisp is easy for a computer to parse, not
| great for humans. There other languages with comparably rich
| metaprogramming that syntactically have a better tradeoff between
| computer and human legibility.
| worewood wrote:
| Yeah, it reads like "parentheses are good, here is a 50-page
| essay on why" - sounds like the author is grasping at straws.
| phoe-krk wrote:
| _> Lisp is easy for a computer to parse, not great for humans._
|
| Which is why "best of both worlds" approaches work best in my
| opinion, no matter if it's "just" automatic indentation and
| paren-counting that leaves actual editing to humans, or more
| intrusive approaches like Parinfer (mentioned in the article)
| that automatically infer parenthesisation from code indentation
| and edit the code as appropriate.
|
| Properly indented Lisp is easy to read (for me), and badly
| indented code can be misread even in languages like C++.
| packetlost wrote:
| Nah, parenthesis are easy to parse with the eye if you spend
| even a small amount of time working with them. You still have
| to indent reasonably like you would with every other syntax.
| gary_0 wrote:
| If I wanted to I could get used to reading Lisp. I could also
| get used to reading Klingon. But I never had to get used to
| reading Python.
| davexunit wrote:
| Speak for yourself, I find the lisp syntax to be among the
| easiest to read.
| norir wrote:
| I am not a lisp hater. There are advantages to its syntax
| relative to other languages. I am just saying that in the
| space of programming languages, there exist other languages
| capable of solving the same problems as lisp in essentially
| the same way using fewer characters simply by removing some
| of the excessive parens and adding syntactic support for the
| most common operations (assignment, scoping, calling
| functions, data structure instantiation). Even if the total
| characters is a wash, not having to chase down dangling
| missed closed brackets or rely on an external tool to
| highlight 15 levels of parens and/or auto insert 10 ugly
| parens at thee end of a nested code block is a nice QOL
| improvement in my opinion.
|
| Again, none of this is saying that lisp is bad. I just think
| we can have nicer things and preserve the important semantic
| insights that lisp gives us.
| camgunz wrote:
| Parens are powerful in lisp(s) because it means the syntax is its
| own AST and can be manipulated by itself (macros). It's one of
| those exploding head moments that you (including me) can get
| totally ensorceled by. But--as with all language features--I
| think pragmatism demands that we ask "what incredible software
| has this produced", and I can't think of any standouts.
| Turing_Machine wrote:
| This site is written in Lisp, I believe (a dialect called Arc).
|
| Not only that, but the site and Y Combinator as a whole likely
| wouldn't exist without Lisp.
|
| https://paulgraham.com/avg.html
|
| (Wow! Over 20 years ago!)
| iainmerrick wrote:
| Has anything else notable been done with Arc?
|
| Rather than "Lisp gives you superpowers", maybe the story is
| actually "there were some very effective developers who
| happened to use Lisp to build their stuff".
| camgunz wrote:
| Yeah but I'm skeptical that there's anything about HN's
| software that's uniquely enabled by being written in Arc. I
| want someone to point to a feature in a Lisp and say, "you
| can write X software only in Z because there's Y feature".
| I'll even accept "significantly more easily" instead of
| "only" here. For example: "you can write secure kernels only
| in Rust because it has direct hardware access and memory
| safety" (let's stipulate this is all true).
| tmtvl wrote:
| There are various problems with 'what incredible software has
| this produced?'.
|
| Various incredible software which has been produced date back
| from before the Lisp machine went the way of the dodo. An
| example of this would be Genera.
|
| Other incredible software has a very specific type of draw
| which doesn't work for everyone. Examples would be Guix and
| Emacs.
|
| And yet other software is very niche, leading to it being
| barely discoverable. As Lisp isn't a hot programming language
| (like Rust is), these projects don't usually get 'this thing
| was programmed in Lisp' coverage. Things like NASA's SPIKE, for
| example (cfr. <https://allegrograph.com/press_room/franzs-
| allegro-cl-used-f...>).
| User23 wrote:
| Also the heavyweights of CAD, which have definitely affected
| your life even if you're not consciously aware, are Lisp
| based.
| camgunz wrote:
| Sure, mainly I mean like, "Lisp's feature X enabled us to do
| thing Y that we couldn't do in other languages which let us
| make baller software Z". I think other languages arguably
| have this stuff: C/C++/Rust have direct hardware access and
| speed, JavaScript has the browser, etc.
|
| It's a little hard for me to pick out exactly what makes
| Allegro CL [0] good at the AI thing. They do say you can also
| write rules in Prolog, which makes me think the advantage
| isn't down to Lisp but rather the library code they wrote on
| top of it.
|
| > Other incredible software has a very specific type of draw
| which doesn't work for everyone. Examples would be Guix and
| Emacs.
|
| Yeah, maybe this is one. I almost listed Emacs, but I'm a
| little torn (also a Vim user so don't trust me here haha).
| Lisp is inextricable from Emacs, but I'm not familiar enough
| to say whether or not you would have an appreciably harder
| time writing an equivalent app in, say, Lua. Maybe! Maybe
| live reload is core to the experience, etc. Like I said I'm
| not super familiar.
|
| So yeah, I should clarify that I don't think the software
| needs to take over the world to meet my criteria here. I
| think niche software can totally qualify; in fact I think
| it's more likely to qualify. Basically it's "we wrote a
| unique, useful program in Lisp that we really couldn't have
| written in a different language." Does Emacs fit the bill
| here? Maybe, but I kind of doubt it.
|
| [0]: https://franz.com/products/allegrocl/
| 1letterunixname wrote:
| And if they had just used an RPN CAS like Erable... ;]
| 10 [ENTER] foo [STO] << foo 32 + >> bar [STO]
| foo bar *
| kleiba wrote:
| > _This code does exactly the same thing as Rust code_
|
| Except it doesn't - because after the final parenthesis of the
| Lisp snippet, the let-binding is finished, i.e., the introduced
| variables are no longer in scope. That is not the case after the
| end of the last line in the Rust example.
|
| The whole point of parentheses is to mark where something starts
| and where it ends. Here, it is where the scope of the let-defined
| variables starts and ends. Without some sort of "parenthesizing",
| there might be a start, but it's much harder to mark where
| something ends.
| sgeisenh wrote:
| I think it's more subtle than that. Rust has a notion of non-
| lexical lifetimes (https://rust-
| lang.github.io/rfcs/2094-nll.html) and the compiler often
| completely avoids the use of the stack for small, trivially
| droppable values, regardless of their lexical scope. In some
| ways, `let` in Rust is a more C-like variant of the `let ... in
| ... end` construct from OCaml.
|
| Not to mention that you can always introduce a new lexical
| scope with `{ ... }` in Rust code.
| User23 wrote:
| I believe there are Common Lisp implementations that will
| happily stack allocate dynamic extent values.
| tmtvl wrote:
| I like Lisp syntax because the following works:
| (if (<= lower-bound position upper-bound) ...)
|
| And the following doesn't: if (lower_bound <=
| position <= upper_bound) ...
| User23 wrote:
| Also (+)
|
| And (*)
|
| Are well defined.
| nyrikki wrote:
| Mostly this article is about people being exposed to
| M-expressions in elementary math.
|
| As someone whos first programming post BASIC was on a Symbolics
| machine, coding _conventions_ worked just fine with s-expressions
|
| The Polish notation is probably what most people are reacting to
| unless you used the reverse version with an HP calculator.
|
| Whitespace isn't significant in C like the author suggests, it is
| coding convention.
|
| COBOL is a counterexample for whitespace significance not helping
| with readability.
|
| But for lisp the parentheses was a non-issue with just a few
| emacs macros.
|
| But that is the same with most modern languages outside of
| novelties like bf
| ur-whale wrote:
| Very long article to find a justification for a Stockholm
| syndrome.
| bfung wrote:
| Lisp code is == to its AST, the programmer is literally writing
| the AST that runs, hence meta programming is really easy.
|
| The trade-off ends up being that metaprogramming becomes harder
| while syntax gets changed
|
| _shrug_ , do it long enough and the parans disappear:
| Q: How can you tell when you've reached Lisp Enlightenment?
| A: The parentheses disappear. ~ Anonymous
| chalcolithic wrote:
| Yet another chance to regret that REBOL has never caught on
| anthk wrote:
| As I use vim/slimv, I don't care, paredit fixes that.
| wduquette wrote:
| Do we though? Do we really need to talk about parentheses?
|
| I've learned a lot from LISP and its variants over the years, and
| I understand the value of LISP syntax for meta-programming, but
| if any of this was truly compelling we'd have all been doing it
| for years.
|
| Every language has its own form of Stockholm Syndrome, I guess.
| valcron1000 wrote:
| Except for the structural editing enabled by the usage of
| parenthesis, all the other features can be achieved without them
| in ex. Haskell: a = let foo = 10
| bar = foo + 32 in foo * bar b = 1
| & (+ 2) & (* 9) & (/ 5)
___________________________________________________________________
(page generated 2024-02-12 23:01 UTC)