This is an entirely new way to misuse "strongly typed" that I was not aware of before. Amazing.
Thank you!
This is an entirely new way to misuse "strongly typed" that I was not aware of before. Amazing.
Thank you!
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
: https://github.com/clojure/clojure/blob/ade22645ba5dbf4c0d8115b19938af96d6fb4cd5/src/clj/clojure/string.clj#L275-L317
(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): 45
My 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: 15
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.
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);}
... or
const hypot = function (x, y) { return Math.sqrt(x ** 2 + y ** 2);};
... or
const hypot = (x, y) => Math.sqrt(x ** 2 + y ** 2);
Note how none of these involve four layers of nested parentheses.
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 a*x + b
should parse the same as a * x + b
, and if it does, then how do you refer to a variable called a*x
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) };
Prost! 🍻
Isn't that how B worked?
Both of those declarations look weird to me. In Haskell it would be:
a :: Stringbob :: (String, Int, Double) -> [String]bob (a, b, c) = ...
... except that makes bob
a function taking a tuple and it's much more idiomatic to curry it instead:
bob :: String -> Int -> Double -> [String]bob a b c = ...-- syntactic sugar for:-- bob = \a -> \b -> \c -> ...
The [T]
syntax also has a prefix form [] T
, so [String]
could also be written [] String
.
OCaml makes the opposite choice. In OCaml, a list of strings would be written string list
, and a set of lists of strings would be string list set
, a list of lists of integers int list list
, etc.
@racketlauncher831 As far as the C compiler is concerned, there is literally no difference between those two notations. If you declare a function parameter as an array (of T), the C compiler automatically strips the size information (if any) and changes the type to pointer (to T).
(And if we're talking humans, then char *args[]
does not mean "follow this address to find a list of characters" because that's the syntax for "array of pointers", not "pointer to array".)
That confirms exactly what tyler said. I'm not sure if you're misreading replies to your posts or misreading your own posts, but I think you're really missing the point.
Let's go through it point by point.
tyler said "JSON Schema is not an ISO standard". As far as I can tell, this is true, and you have not presented any evidence to the contrary.
tyler said "JSON Data Interchange Format is a standard, but it wasn’t published until 2017, and it doesn’t say anything about 1.0 needs to auto cast to 1". This is true and confirmed by your own link, which is a standard from 2017 that declares compatibility with RFC 8259 (tyler's link) and doesn't say anything about autocasting 1.0 to 0 (because that's semantics, and your ISO standard only describes syntax).
tyler said "JSON Schema isn’t a specification of the language, it’s for defining a schema for your code", which is true (and you haven't disputed it).
Your response starts with "yes it is", but it's unclear what part you're disagreeing with, because your own link agrees with pretty much everything tyler said.
Even the part of the standard you're explicitly quoting does not say anything about 1.0 and 1 being the same number.
Why did you bring up JSON Schema (by linking to their website) in the first place? Were you just confused about the difference between JSON in general and JSON Schema?
@masterspace Love the confidence, but your facts could do with some work.
"Interpreted language" is not a thing. Interpreted vs compiled is a property of a particular implementation, not the language.
(I wonder how you would classify lazy functional languages like Haskell. The first implementations were all interpreters because it was not clear that the well-known graph reduction technique for lazy evaluation could be compiled to native code at all.Today, hugs (a Haskell interpreter written in C) isn't maintained anymore, but GHC still comes with both a native compiler (ghc) and an interpreter (runghc, ghci).)
Most implementations that employ interpretation are compiler/interpreter hybrids. It is rare to find an interpreter that parses and directly executes each line of code before proceeding to the next (the major exception being shells/shell scripts). Instead they first compile the source code to an internal representation, which is then interpreted (or, in some cases, stored elsewhere, like Python's .pyc
files or PHP's "opcache").
You can tell something is a compile-time error if its occurrence anywhere in the file prevents the whole file from running. For example, if you take valid code and add a random {
at the end, none of the code will be executed (in Javascript, Python, Perl, PHP, C, Haskell, but not in shell scripts).
The original "lint" tool was developed because old C compilers did little to no error checking. (For example, they couldn't verify the number and type of arguments in function calls against the corresponding function definitions.) "Linting" specifically refers to doing extra checks (some of which may be more stylistic in nature, like a /*FALLTHRU*/
comment in switch statements) beyond what the compiler does by itself.
If I refer to an undeclared variable in a Perl function, none of the code runs, even if the function is defined at the end of the file and never called. It's a compile-time error (and I don't have to install and configure a linting tool first). The same is true of Haskell.
What's funny to me is that Javascript copied use strict
directly from Perl (albeit as a string because Javascript doesn't have use
declarations), but turned it into a runtime check, which makes it a lot less useful.
Yes, they do: https://rr-project.org/
https://sourceware.org/gdb/current/onlinedocs/gdb.html/Reverse-Execution.html