4 ms·
Using parsing and abstractions for safety is the basis of why Clojure, even though dynamically typed, is a relatively safe language[1]. The abstractions are saf
by didibus 6y ago
Using parsing and abstractions for safety is the basis of why Clojure, even though dynamically typed, is a relatively safe language[1]. The abstractions are safer in Clojure: no for loops, no while loops, only higher level iteration and transformation constructs, data interfaces are all immutable and functional. Shared state can only be used through higher level managed APIs, numbers auto-promote themselves to higher precision, there's no linear search APIs to force the use of indexed data-structures instead, etc.
And for parsing: Spec is a way to define contracts, but it's actually a parser DSL. User defined macros which specify a Spec will have their input parsed by the Spec at read-time, and if the Parser fails, a read error will show up. Unfortunately, this only happens for macro, but the article made me curious about possible value of having this done on functions as well.
An example of this is say you have some macro which would let you do:
(:from coll :take 5)
Your Spec can parse this, and error if the first element isn't :from, the second element isn't a reference or a collection literal, the third element isn't :take, the fourth element isn't a reference or an int, as well as if it isn't called with exactly 4 elements. And all this happens at read time, when the code is parsed. You could go further, and if a collection literal was used, you could assert that the take int isn't bigger then the number of elements in the collection literal.
Now, what I've observed is that reference types kind of limit what the parser can do. I'm not sure the article addresses that. Like in my last example, I can validated the range of take over the size of the collection, but not if the collection is a variable. How do I track those references? Maybe somewhere else in the program you can find the collection literal, but not here.
P.S.: It's hard to know why Clojure demonstrates relative safety, and I assume here it has a little bit to do with the abstractions it uses and this parser Specifying mechanism. But it's also likely it's all due to the REPL or something else.
[1] https://www.i-programmer.info/news/98-languages/11184-which-languages-are-bug-prone.html https://www.i-programmer.info/news/98-languages/11184-which-... - Shows Clojure as having some of the least number of defects per commits
- deleted 6y ago[deleted]
- archgoon 6y agoClojure, for those unfamiliar with the language, unsurprisingly, does in fact allow for both 'for' and 'while' loops, and are part of the core language library. https://clojuredocs.org/clojure.core/while https://clojuredocs.org/clojure.core/while https://clojuredocs.org/clojure.core/for https://clojuredocs.org/clojure.core/for And here are examples picked from a random repository of these constructs being used in the wild: https://github.com/ring-clojure/ring/search?q=while https://github.com/ring-clojure/ring/search?q=while What clojure does do is also provide a number of higher level control constructs, and, by the nature of the syntax of the language, puts them on the same level as 'while' and 'for', which themselves are part of a library, rather than baked into the language itself. This can, of course, be done in other languages, using fluent interfaces. https://en.wikipedia.org/wiki/Fluent_interface https://en.wikipedia.org/wiki/Fluent_interface But is typically much more awkward and harder to compose. Also, you typically have to wrap expressions in lambdas.
- didibus 6y agoNot to be too nit-picky, but it doesn't actually have a for-loop. The for you linked to is not a for-loop but a for-each loop (simply called for). Well it is actually more restricting even then a for-each loop, as it maps over elements and can only return a collection of the same length as it iterates over. That said, you're correct it does have a pseudo while-loop. Though it will still force you to use a managed mutable container along with it, which is still safer than without. Also, it's a very ackward while-loop, as it doesn't support continue or break, that's because in reality it isn't a real while-loop, it's a fake while-loop built on top of a recursive loop. And for all other readers, it's true, Clojure doesn't disallow the use of less abstract less safe construct, it simply makes them more ackward to use and not the default and obvious choice. The reason for this is also something the article didn't bring up, but higher level safer abstractions often come with a performance cost. This is why most languages have escape hatches for whatever safety mechanism they provide, and why Rust lang loves to talk about "zero-cost" abstractions.
- dkersten 6y agoClojure's "for" is really a list comprehension function, but Clojure's "doseq" is more or less what you'd expect from a for loop (like Python's anyway or any other range-based for).