Rich Hickey: When a library breaks, it can break in many ways. Some of those may or may not be manifest in types, others would just be manifest in behavior, or missing information, or additional requirements - things that you can’t express in types, because most of what your program needs to do can’t be expressed in the type systems we have today. So yes, it still takes a string and still returns a map of information, but it stopped returning you some of that information, or it started returning other stuff, or it had additional requirements about the string… No, the types don’t capture that.
It seems like every talk or interview coming from Rich ever since Cognitect started hawking clojure.spec contains at least a handful of poorly supported, vague, dogmatic claims about static typing in opposition to…well, “how Clojure does it.”
Perhaps his style hasn’t bothered me up until recently simply because I’ve mostly agreed with his dogmatic statements about stuff like persistent data structures, but at this point I’ve lost interest in a lot of what he has to say because of how he talks about static typing.
There are a lot of other ways he could be talking about clojure.spec and why it works well in Clojure. A more nuanced appraisal of the trade-offs of clojure.spec vs. various static typing approaches would be a nice start, but I am skeptical that will happen any time soon.
I still think Clojure is a better language than many, and gets a lot of things right, but it’s not perfect and this is one area I think the creator is mistaken. While that’s fine and to be expected, I think he’s doing real damage by making statements about static typing that are either too vague to be useful, or misrepresent how static-typing advocates think about and use type systems.
It seems like every talk or interview coming from Rich ever since Cognitect started hawking clojure.spec contains at least a handful of poorly supported, vague, dogmatic claims about static typing in opposition to…well, “how Clojure does it.”
I’m not too familiar with clojure.spec, but I know a bit about the overall idea of it. Let me see if I can explain the difference between static types vs clojure.spec from a formal methods perspective (which differs a little from the PLT perspective).
Clojure.spec is a contract system. A contract is a formally specifiable property of the code, usually as pre/postconditions on functions, that you expect the code to follow. You’re able to, through the contracts alone, specify the complete behavior of the function via specs. For example, using Deadpixi Contracts in Python:
@require("l must not be empty", lambda args: len(args.l) > 0)
@ensure("result is head of list", lambda a, result: result + tail(a.l) == a.l)
def head(l: List[T]) -> T:
return l[0]
With this we have that head is only specified for nonempty lists, and also that its result will always be, in fact, the head of the list. This is not something we can do with the type system of Python, or even the type system of Haskell, as the type of head is indistinguishable from the type of last. We’re calling all sorts of other functions and can, in fact, run arbitrary code. In fact, we can completely decouple the specification of contracts from the verification of them. This is both a strength and a weakness. The strength is that we get both expressive power and flexibility. The weakness is that expressive power is usually a bad thing. In the general case contracts are unverifiable due to the halting problem.
In practice, there’s four approaches to verifying contracts:
Restrict yourself to a subset where you have simple, automatic static verification. For example, you might not be able to autoverify “this function will be called only with positive even numbers”, but we can autoverify “this function will be called with only integers”. This gives us static typing! I think you could reasonably argue that “type systems are special cases of contract systems”, but that’d get you stabbed to death in 99% of programming forums out there, so uh yeah
Limit verification to throwing runtime errors. Every time a contract comes up, check if it’s correct, and throw an error if isn’t. Most contract-oriented languages combine this with static typing to get “conventional” contract systems. You can do a lot of cool stuff with this. Eiffel’s AutoTest can turn your runtime contracts into integration tests, Ada can place contracts on global mutations, most systems let synthesize contracts into dynamic types, etc.
Formally prove the contracts correct. This is formal verification. A lot of people are doing this in different ways: Dafny uses pre/postconditions and loop invariants, Liquid Haskell uses refinement types, Idris uses dependent types, etc.
Informally prove the contracts correct. This is how we get Cleanroom, which is actually a lot more effective than you’d think. People write the contracts, attach english “proofs” of why they hold, and everybody verifies them through code review.
So, in summary: contracts generalize static types in a form that is good in some ways, bad in others. There are multiple different styles of contracts, just as there’s multiple different type system, but the unifying idea is that they can fully specify the program’s behavior. In verifying them is another matter, and clojure.spec’s approach is “runtime checks” as opposed to most other languages, which reduce the specification power in return representing contracts as static types.
It seems like every talk or interview coming from Rich ever since Cognitect started hawking clojure.spec contains at least a handful of poorly supported, vague, dogmatic claims about static typing in opposition to…well, “how Clojure does it.”
I haven’t been paying all that much attention to the talks more recently, but this is also how I feel about it. There are trade-offs between static types and specs, and specs have some advantages over types, but because of all the straw-man arguments it’s hard to find useful analyses of the trade-offs.
The problem is that most of the “types vs ‘specs’” arguments are between people who have languages with types and no ‘specs’ and people who have languages with ‘specs’ and no types. If you want to see a more nuanced comparison, you have to look at languages that have both of them, because then you can see how people proficient in both context-switch between them.
The other problem is that there are many more languages with a lot of thought to their type system than languages with a lot of thought to their contract system. If you need both, you’re pretty much limited to Ada.
I fail to see what’s vague or dogmatic about his statements. He’s basically saying that types primarily focus on checking internal self consistency, while what you really care about is semantic correctness. Expressing semantic correctness using types ranges from being difficult to impossible depending on the type system. At the same time static typing can introduce a lot of complexity that’s incidental to the problem being solved. You often end up writing code in a way that facilitates static verification as opposed to human readability.
It seems like every talk or interview coming from Rich ever since Cognitect started hawking
clojure.speccontains at least a handful of poorly supported, vague, dogmatic claims about static typing in opposition to…well, “how Clojure does it.”Perhaps his style hasn’t bothered me up until recently simply because I’ve mostly agreed with his dogmatic statements about stuff like persistent data structures, but at this point I’ve lost interest in a lot of what he has to say because of how he talks about static typing.
There are a lot of other ways he could be talking about
clojure.specand why it works well in Clojure. A more nuanced appraisal of the trade-offs ofclojure.specvs. various static typing approaches would be a nice start, but I am skeptical that will happen any time soon.I still think Clojure is a better language than many, and gets a lot of things right, but it’s not perfect and this is one area I think the creator is mistaken. While that’s fine and to be expected, I think he’s doing real damage by making statements about static typing that are either too vague to be useful, or misrepresent how static-typing advocates think about and use type systems.
I’m not too familiar with clojure.spec, but I know a bit about the overall idea of it. Let me see if I can explain the difference between static types vs clojure.spec from a formal methods perspective (which differs a little from the PLT perspective).
Clojure.spec is a contract system. A contract is a formally specifiable property of the code, usually as pre/postconditions on functions, that you expect the code to follow. You’re able to, through the contracts alone, specify the complete behavior of the function via specs. For example, using Deadpixi Contracts in Python:
With this we have that
headis only specified for nonempty lists, and also that its result will always be, in fact, the head of the list. This is not something we can do with the type system of Python, or even the type system of Haskell, as the type ofheadis indistinguishable from the type oflast. We’re calling all sorts of other functions and can, in fact, run arbitrary code. In fact, we can completely decouple the specification of contracts from the verification of them. This is both a strength and a weakness. The strength is that we get both expressive power and flexibility. The weakness is that expressive power is usually a bad thing. In the general case contracts are unverifiable due to the halting problem.In practice, there’s four approaches to verifying contracts:
So, in summary: contracts generalize static types in a form that is good in some ways, bad in others. There are multiple different styles of contracts, just as there’s multiple different type system, but the unifying idea is that they can fully specify the program’s behavior. In verifying them is another matter, and
clojure.spec’s approach is “runtime checks” as opposed to most other languages, which reduce the specification power in return representing contracts as static types.It’s worth noting that the halting problem also affects Turing complete type systems, such as one found in Scala.
Which is a reason you really don’t want your type system to be Turing complete!
I haven’t been paying all that much attention to the talks more recently, but this is also how I feel about it. There are trade-offs between static types and specs, and specs have some advantages over types, but because of all the straw-man arguments it’s hard to find useful analyses of the trade-offs.
The problem is that most of the “types vs ‘specs’” arguments are between people who have languages with types and no ‘specs’ and people who have languages with ‘specs’ and no types. If you want to see a more nuanced comparison, you have to look at languages that have both of them, because then you can see how people proficient in both context-switch between them.
The other problem is that there are many more languages with a lot of thought to their type system than languages with a lot of thought to their contract system. If you need both, you’re pretty much limited to Ada.
Relevant: https://ro-che.info/ccc/17
I prefer this version: https://pbs.twimg.com/media/DZWGpq2VAAE2DI7.jpg
(from here)
I fail to see what’s vague or dogmatic about his statements. He’s basically saying that types primarily focus on checking internal self consistency, while what you really care about is semantic correctness. Expressing semantic correctness using types ranges from being difficult to impossible depending on the type system. At the same time static typing can introduce a lot of complexity that’s incidental to the problem being solved. You often end up writing code in a way that facilitates static verification as opposed to human readability.
Transcript of the talk http://www.case-podcast.org/20-problem-solving-and-clojure-19-with-rich-hickey/transcript