all 22 comments

sorted by: hot top controversial new old
[–] 9 points 1 year ago* (2 children)

I did indeed have a chuckle, but also, this shouldn't be too foreign compared to other, more-popular languages. The construction of func param1 param2 can be found in POSIX shell, with Bash scripts regularly using that construction to pass arguments around. And although wrapping that call with parenthesis would create a subshell, it should still work and thus you could have a Lisp-like invocation in your sh script. Although if you want one of those parameters to be evaluated, then you're forced to use the $() construction, which adds the dollar symbol.

As for Lisp code that often looks like symbol soup, like (= 0 retcode), the equal-sign is just the name for the numerical equality function, which takes two numbers. The idea of using "=" as the function name should not be abnormal for Java or C++ programmers, because operator overload allows doing exactly that.

So although it does look kinda wonky for anyone that hasn't seen Lisp in school, sufficient exposure to popular codebases and languages should impart an intuition as to how Lisp code is written. And one doesn't even need to use an RPN calculator, although that also aids understanding of Lisp.

Addendum: perhaps in a century, contemporary programmers will find it bizarre that C used the equal-sign to mean assignment rather than equality, when the <= arrow would more accurately describe assignment, while also avoiding the common error of mixing up = and == in an if-conditional. What looks normal today will not necessarily be so obvious in hindsight.

  • source
  • hideshow 2 child comments
  • [–] 5 points 1 year ago (1 child)

    perhaps in a century, contemporary programmers will find it bizarre that C used the equal-sign to mean assignment rather than equality, when the <= arrow would more accurately describe assignment

    Maybe they'll find a better symbol than the one we already use for less than or equal to.

  • source
  • parent
  • hideshow 1 child comment
  • [–] 1 point 1 year ago* (last edited 1 year ago) (12 children)

    Notice that they have the same number of parenthesis. People say that lisps have a lot of parentheses, but they have the same number as any other language that uses parentheses to make function calls.

  • source
  • hideshow 12 child comments
  • [–] [S] 5 points 1 year ago

    I find one huge advantage of Lisp syntax is that you can visually see relationships in code by following the nesting. It's like having a diagram embedded for free once you get used to reading it.

  • source
  • parent
  • [–] 8 points 1 year ago (9 children)

    This is false. Lisp uses parentheses where other languages use (), [], {} or nothing at all. For example, in C I can write int i = 0, but the equivalent Lisp code involves three pairs of parentheses. Or take something like a[i] = f(i + 1) / 2 + p. The equivalent Lisp code is something like (setf (aref a i) (+ (/ (f (+ i 1)) 2) p)), and you can't tell me that's not a lot of parentheses.

  • source
  • parent
  • hideshow 9 child comments
  • [–] [S] 4 points 1 year ago (8 children)

    This is false, Lisp is a family of languages. Clojure is an example of a Lisp where you have different types of literals using () for lists, [] for vectors, {} for maps, and #{} for sets. Furthermore, there are plenty of libraries for doing infix syntax which can be trivially expressed using macros. For example, infix library in Clojure lets you write math using infix syntax. That said, it's pretty rare that you're actually writing a bunch of math expressions in regular code unless you're working in a specific domain, so this hardly comes up in practice.

  • source
  • parent
  • hideshow 8 child comments
  • [–] 2 points 1 year ago (7 children)

    OK, my code snippets are Common Lisp. But note that none of them involve list/vector/set literals. I was thinking of [] for array indexing and {} for code blocks.As for infix macros, sure, that's not hard to do, but it's not built into the language and there being "plenty of libraries" is part of the problem: They're all subtly different, none are standard, and I suspect most people don't use them anyway. (Plus there's fun little design issues like whether ax + b should parse the same as a * x + b, and if it does, then how do you refer to a variable called ax from an infix environment?)It doesn't solve the main issue anyway. Take this snippet from the "infix" readme:(def hypot (fn [x y] (infix sqrt(x ** 2 + y ** 2))))It ends with a cluster of )))) (reinforcing the "lots of parentheses" impression) and all of those parentheses mean something different: From the outside in, we have the end of a symbol definition (def ...), the end of a function (fn ...), the end of a macro invocation (infix ...), and the end of a function call sqrt(...). It definitely isn't just "the same number [of parentheses] as any other language that uses parentheses to make function calls".Compare e.g. these versions written in Haskell:hypot = \x y -> sqrt (x ** 2 + y ** 2)... or Perl:sub hypot($x, $y) { sqrt($x ** 2 + $y ** 2)}... or if you want to separate the function and symbol definition parts:*hypot = sub ($x, $y) { sqrt($x ** 2 + $y ** 2) };

  • source
  • parent
  • hideshow 7 child comments
  • [–] [S] 1 point 1 year ago (6 children)

    OK, my code snippets are Common Lisp. But note that none of them involve list/vector/set literals. I was thinking of [] for array indexing and {} for code blocks.

    Again, Clojure uses vectors for arguments, so you end up with syntax like this which provides the same visual information as any mainstream language.

    It doesn’t solve the main issue anyway. Take this snippet from the “infix” readme:

    Yes it does actually because you have syntax hints indicating the type of data structure you're looking at. For example, in the snippet you highlight, the function arguments are in a vector.

    It ends with a cluster of )))) (reinforcing the “lots of parentheses” impression) and all of those parentheses mean something different

    First of all, you have exact same amount of parens as you would in a mainstream language like Java, C, or Js. Second, the parens do mean the same thing in that example. The big benefit with s-exps though is that you have structural editing, and you don't actually manage parens by hand or even think about them. You treat expressions as building blocks that you manipulate and compose together. There's nothing even remotely comparable to this available in languages like Haskell.

    However, here's an example for you where you don't have same parens.

    (defn foo [{:keys [x y]}]
      (let [z (str x " " y)]
        {:result z}))
    

    here you have different data structures manipulated, and you have different parens representing them.

    From the outside in, we have the end of a symbol definition (def ...), the end of a function (fn ...), the end of a macro invocation (infix ...), and the end of a function call sqrt(...). It definitely isn’t just “the same number [of parentheses] as any other language that uses parentheses to make function calls”.

    It's just broken down in the example. In practice you have defn and you just write:

    (defn hypot [x y]  (infix sqrt(x ** 2 + y ** 2)))
    

    The huge advantage over Haskell is that syntax is very simple and regular. You don't have to think about it at all. Languages like Haskell and Perl introduce a lot of mental overhead because you have to memorize all the special cases and behaviors the syntax introduces. You can write really concise code, but there's a cost to it. There's a reason people refer to Perl as a write only language. On the other hand, Clojure hits the sweet spot of being very concise without needing a complex syntax.

  • source
  • parent
  • hideshow 6 child comments
  • [–] 2 points 1 year ago (5 children)

    I disagree with pretty much everything you write here, but especially this:First of all, you have exact same amount of parens as you would in a mainstream language like Java, C, or Js.My Perl example uses "mainstream language" syntax. Apparently that doesn't count because it's Perl (scary! mental overhead! write only!), so here's exactly the same thing in JavaScript:function hypot(x, y) { return Math.sqrt(x ** 2 + y ** 2);}... orconst hypot = function (x, y) { return Math.sqrt(x ** 2 + y ** 2);};... orconst hypot = (x, y) => Math.sqrt(x ** 2 + y ** 2);Note how none of these involve four layers of nested parentheses.

  • source
  • parent
  • hideshow 5 child comments
  • [–] [S] 1 point 1 year ago* (4 children)

    The total number of parens in your examples is about the same, except you also have a bunch of noise like random semicolons sprinkled in. Meanwhile, nesting is a feature, not a bug because it provides you with additional visual information about relationships in code. The only people who incessantly bray about nested parens are the ones who've never actually worked with Lisp for any period of time. If this was a genuine problem with the syntax then stuff like sweet expressions would've taken off. The reality is, as I already explained and you've ignored, is that the editor manages the parens for you. When you work with Lisp, typing opening paren is equivalent to having a command sequence to say I'm starting a new expression.

  • source
  • parent
  • hideshow 4 child comments
  • [–] 1 point 1 year ago (3 children)

    Let's take a step back. I feel this discussion has got off track a bit.The original claim was that Lisp's reputation as having lots of parentheses was undeserved because it uses the same number of parens as other languages that use () for function calls; Lisp just puts the parens in a different place.My objection was basically that Lisp also uses parentheses for what in other languages is a declaration, a statement, a block, an operator, etc so just looking at function calls doesn't give you the whole picture.You said that "Lisp" is actually a family of languages, that Clojure uses fewer parens than other Lisps (I object: still more than non-Lisps), that there are macros for infix expression syntax (I object: non-standard/3rd-party solutions that only help with operators), that parens don't even matter because of structural editing tools (I object: irrelevant, the discussion was about the number of parens, not whether they "matter").I also disagree with "the total number of parens in your examples is about the same". This is a micro-example, so when the original Lisp (Clojure) code has 4 pairs of parens and the C-style (JavaScript, Perl) version has 2, that's twice the parentheses to me, not "about the same".I've tried to find a slightly bigger code sample, so I clicked around in the Clojure standard library. Here's a chunk of clojure.string: github.com/clojure/clojure/blo(defn ^String trim-newline "Removes all trailing newline \n or return \r characters from string. Similar to Perl's chomp." {:added "1.2"} [^CharSequence s] (loop [index (.length s)] (if (zero? index) "" (let [ch (.charAt s (dec index))] (if (or (= ch \newline) (= ch \return)) (recur (dec index)) (.. s (subSequence 0 index) toString))))))(defn blank? "True if s is nil, empty, or contains only whitespace." {:added "1.2"} [^CharSequence s] (if s (loop [index (int 0)] (if (= (.length s) index) true (if (Character/isWhitespace (.charAt s index)) (recur (inc index)) false))) true))(defn ^String escape "Return a new string, using cmap to escape each character ch from s as follows: If (cmap ch) is nil, append ch to the new string. If (cmap ch) is non-nil, append (str (cmap ch)) instead." {:added "1.2"} [^CharSequence s cmap] (loop [index (int 0) buffer (StringBuilder. (.length s))] (if (= (.length s) index) (.toString buffer) (let [ch (.charAt s index)] (if-let [replacement (cmap ch)] (.append buffer replacement) (.append buffer ch)) (recur (inc index) buffer)))))Total number of pairs of parentheses (not counting doc-strings): 45My translation of the code to JavaScript:function trim_newline(s) { for (let index = s.length; index > 0; index--) { let ch = s.charAt(index - 1); if (ch != '\n' && ch != '\r') { return s.substr(0, index); } } return "";}function blank(s) { if (s == null) { return true; } for (let index = 0; index < s.length; index++) { if (!Character.isWhitespace(s.charAt(index))) { return false; } } return true;}function escape(s, cmap) { let buffer = ""; for (let index = 0; index < s.length; index++) { let ch = s.charAt(index), replacement = cmap(ch); buffer += replacement !== undefined ? replacement : ch; } return buffer;}Total number of pairs of parentheses: 15That's about a factor of 3. Now, you can argue that I shouldn't care about the parentheses, but my point is simply that they're there and you can't really avoid them if you write in anything resembling idiomatic Lisp.

  • source
  • parent
  • hideshow 3 child comments
  • [–] [S] 1 point 1 year ago* (2 children)

    You said that “Lisp” is actually a family of languages, that Clojure uses fewer parens than other Lisps (I object: still more than non-Lisps), that there are macros for infix expression syntax (I object: non-standard/3rd-party solutions that only help with operators), that parens don’t even matter because of structural editing tools (I object: irrelevant, the discussion was about the number of parens, not whether they “matter”).

    Clojure, uses different types of syntax to denote different types types of data structures, and thus different types of operations, such as statement declaration. I even gave you examples showing that you get the exact same amount of visual information as you do in other languages.

    Meanwhile, you object to the very nature of how Lisp works. The whole point Lisps is that you allow the user to easily create whatever semantics they want using macros. This is something that's not possible to do in most languages. If you want some new semantics or syntax you have to wait for the language committee to agree on that.

    The big advantage here is that the core language can stay small and focused without having to grow over time. This is what you see happening in practically all mainstream languages. Usage patterns change, applications of the language change, and as a result new syntax continues being bolted on which makes the language ever bigger and harder to learn.

    On the other hand, with Lisp approach, you have a small language that's simple and consistent while libraries express different semantics that happen to be needed at the time. As a user you only have to learn the semantics of the libraries used in the project you're working on. As different patterns fall out of use, they don't accrete within the core language. New users don't need to know about random quirks that were popular a decade ago.

    Your second objection is also nonsensical because these is how the language used in practice. Pretty much nobody would write code without syntax highlighting, and similarly you wouldn't use s-expressions based language without structural editing. This is a made up argument that's a complete non sequitur. I've even showed you sweet expression alternative syntax that exists and literally nobody uses. If your argument had any merit to it, then that's what Lispers would use.

    I also disagree with “the total number of parens in your examples is about the same”. This is a micro-example, so when the original Lisp (Clojure) code has 4 pairs of parens and the C-style (JavaScript, Perl) version has 2, that’s twice the parentheses to me, not “about the same”.

    const hypot = (x, y) => Math.sqrt(x ** 2 + y ** 2);
    

    4 parens, 1 ,,1 =, 1 ;, 1 =>, 1 =

    (defn hypot [x y]  (infix sqrt(x ** 2 + y ** 2)))
    

    6 parens, 1 [

    Clojure version is more consistent with less noise. Ignoring all the other noise you have to add is frankly intellectually dishonest. Whether it's a paren or a comma, or a semicolon, an arrow or an equals sign, it's still syntax. Clojure version has less syntax. That's literally less stuff I need to parse visually and worry about when writing code.

    Let's take a look at your JavaScript version more closely, and it immediately becomes obvious that you're not actually saving anything here. You just end up sprinkling more types of control characters in it:

    9 {, 15 (, 12 = (for assignment), 16 ; 3 ,, 1 ?, :`

    That’s about a factor of 3. Now, you can argue that I shouldn’t care about the parentheses, but my point is simply that they’re there and you can’t really avoid them if you write in anything resembling idiomatic Lisp.

    Yeah if you just ignore all the other control characters you had to add instead then you sure saved on parens. Bravo!

    Your whole argument is basically treating parens as something different from other syntax characters, which is a nonsensical argument. If you're going to compare syntax then you have to compare all the syntax you have. The only type of syntax that will give you significant reduction would be white space based syntax as seen in Python or Haskell, but that adds its own problems hence why most languages avoid it.

  • source
  • parent
  • hideshow 2 child comments
  • [–] 1 point 1 year ago (1 child)

    Yeah if you just ignore all the other control characters you had to add instead then you sure saved on parens. Bravo!Yes! Exactly! Thank you; that was my whole point.Your whole argument is basically treating parens as something different from other syntax characters, which is a nonsensical argument. If you’re going to compare syntax then you have to compare all the syntax you have. I'm not trying to compare syntax in general. All I'm doing is refuting the original claim, which was that Lisp doesn't use more parentheses than other ("conventional") languages. It does.All that stuff about syntactic noise, granted. I mean, I don't agree personally, but it's irrelevant to my point.

  • source
  • parent
  • hideshow 1 child comment
  • [–] [S] 1 point 1 year ago

    Again, it's very weird to single out one type of control character. You have to compare syntax trade offs holistically. However, even when you straight up compare parens, there isn't a significant difference. Pretty much every language, except those using whitespace, will have two parens for arguments and curlies for the function body, or a statement. This is roughly the same number of parens you end up with in Clojure, minus all the other characters. The difference is just not that dramatic in practice.

  • source
  • parent
  • [–] 1 point 1 year ago* (1 child)

    I mean, which one did you learn in math class? Not saying it's better, but it's way more common.

  • source
  • hideshow 1 child comment
  • [–] -1 points 1 year ago (3 children)

    the fuck is lisp and this syntax?

  • source
  • hideshow 3 child comments
  • 23 comments in c/programmer_humor@programming.dev view that post

    WDYM "the 1st time"?

  • source
  • [–] 7 points 1 year ago (17 children)

    Where does that notation work?

  • source
  • hideshow 17 child comments
  • [–] 42 points 1 year ago (4 children)

    Lisp uses it, with the fun extra part that operators are just normal functions - so instead of foo(bar) you get (foo bar), or for operators 1+1+2 becomes (+ 1 1 2). It’s a really fun language even just for being different than most, I def recommend playing around with it if you’re looking for something new.

  • source
  • parent
  • hideshow 4 child comments
  • [–] 14 points 1 year ago (3 children)

    The most interesting part about Lisp is homoiconicity:

    (+ 1 1 2) is literally a list with symbol "+" and 3 numbers.

    Which allows to build the most powerful macro possible, manipulating code (with data as a tree-like structures) and changing it into whatever else at compile time.

    Now if only there was any good use for macros, this would be the best language 🙃

  • source
  • parent
  • hideshow 3 child comments
  • [–] 11 points 1 year ago (2 children)

    Threading is a great case for a macro.

    (-> x (* 2) (/ 3) (- 1))

    Is the same as (- (/ (* x 2) 3) 1)

  • source
  • parent
  • hideshow 2 child comments
  • [–] 1 point 1 year ago (1 child)

    Sure it's not like it has no uses, but it's something languages have built-in as syntax sugar or operators, and you rarely need to built your own macro for anything.

  • source
  • parent
  • hideshow 1 child comment
  • load more comments (-1 replies)
  • [–] 8 points 1 year ago (8 children)

    (f x) works this way in Lisp - as in the joke - and Lisp descendants like Scheme. And then there's Haskell which takes the whole thing a step further still.

    Also Perl, because Larry thought it would be fun(ctional). The external parentheses are technically optional in this case, but won't break anything if included. Regular f(x) syntax is also supported there. (You could probably remake this meme with Python and Perl in first and second panels tbh.)

    And I know of at least one dialect of BASIC that allowed subroutine calls to lack their parentheses, so the same external parentheses thing would apply if that subroutine was a function.

  • source
  • parent
  • hideshow 8 child comments
  • [–] 4 points 1 year ago (4 children)

    And then there’s Haskell which takes the whole thing a step further still.

    Wait, what works in Haskell that doesn't in Lisp, exactly? Are the spaces not just function composition?

  • source
  • parent
  • hideshow 4 child comments
  • [–] 3 points 1 year ago (3 children)

    My mistake. I had somehow missed or forgotten that Lisp also supports currying, which is what I was thinking of as Haskell taking further. There might be other things regarding type declaration and such, but that's a little beyond me to confirm or deny at the moment.

  • source
  • parent
  • hideshow 3 child comments
  • [–] 3 points 1 year ago (1 child)

    In haskell, any function with a name made entirely out of symbols (like + or >>=) works as an infix operator (you can turn any function into an infix operator using backticks)

  • source
  • parent
  • hideshow 1 child comment
  • [–] 1 point 1 year ago

    Now that you mention it, I do remember the backticks and symbols thing for infix, so yeah that'd be something extra that Haskell did. One of the few things about Haskell that wasn't on the fringes of my capability and understanding as I recall.

    I remember thinking that it would be cool if other, more procedural, languages allowed it, but then most other languages also don't have the capability of setting the precedence of new operators relative to old ones on the fly. A lot of that stuff is hard-coded into those languages' compilers.

  • source
  • parent
  • [–] 3 points 1 year ago

    In Haskell, all functions are curried by default, so you can partially apply a function merely by applying it to fewer than the supported number of arguments.

    Also, it is worth noting that laziness-by-default in Haskell makes it so that you can use ordinary functions to define control structures, rather than needing to turn to metaprogramming like you do in Lisp.

  • source
  • parent
  • [–] 2 points 1 year ago (2 children)

    of at least one dialect of BASIC that allowed subroutine calls to lack their parentheses

    Did sub calls normally have parentheses in BASIC?

  • source
  • parent
  • hideshow 2 child comments
  • [–] 1 point 1 year ago (1 child)

    Yes. Most early BASICs even required that any reference to a function name, in definition or calling, be preceded by an FN keyword as well as the parentheses.

    QBASIC, Visual BASIC and the related dialects of BASIC found in MS Office and LibreOffice all have slightly better syntax for defining and calling functions than the older BASICs, but they all still require parentheses on their subroutine parameter lists too.

    At best, you might be able to call a subroutine by name with no empty parentheses after it, but as soon as you need parameters, you'll need parentheses around them.

    But like I say, there was at least one rare BASIC that didn't need them, so I'm assuming there might have been others that I'm not aware of.

  • source
  • parent
  • hideshow 1 child comment
  • [–] 1 point 1 year ago

    Thanks for the info. I haven't used BASIC in a very long time and can't remember much about the syntax. Must be over 20 years ago now. I switched from VB6 to C# when .NET Framework 2.0 was released.

  • source
  • parent
  • [–] 4 points 1 year ago (1 child)

    What about the M-expression version (f[x])?

  • source
  • hideshow 1 child comment
  • [–] 4 points 1 year ago (1 child)

    Does that make Lisp a language with significant white space?

  • source
  • hideshow 1 child comment