7 ms·
The definition of breaking change here completely destroys any idea of abstraction or bug fixes. If calling a function requires knowing how the function is impl
by fiddlerwoaroof 3y ago
The definition of breaking change here completely destroys any idea of abstraction or bug fixes. If calling a function requires knowing how the function is implemented, then the function should not exist in the first place: the whole point of an abstraction is that, if the use-site fulfills the precondition, it can assume that the postcondition holds after it used the abstraction.
This means that relaxing the precondition is, by definition, not a breaking change because the old inputs are a subset of the new ones. Similarly, strengthening the postcondition isn’t a breaking change because the new outputs are a subset of the old ones. (If and he use-site assumes that it gets a specific member of the function’s codomain for specific input values, you have an abstraction leak and the use-site has a bug [or the function should be inlined because it’s a false abstraction])
- _madmax_ 3y agoI agree with you wholeheartedly
- sanderjd 3y agoMy first thought was, "I wonder what the author considers a non-breaking change..."
- squirtlebonflow 3y agoComments, updating dependencies, or documentation
- sanderjd 3y agoUpdating dependencies could way more easily be a breaking change than what we're discussing... And comments and documentation aren't code changes at all.
- hn_throwaway_99 3y agoI really wish I had the ability to downvote articles. Agree with you 100%, and I feel like I lost braincells from reading this sentence: > You are changing the behavior of the function. That is a breaking change. So this guy is declaring someone else "wrong" because he unilaterally decides to redefine what a "breaking change" means? This is one of the dumbest "all Internet arguments are over semantics" examples I can think of. As an aside, I've seen a number of recent instances (here's another, https://news.ycombinator.com/item?id=36826111 https://news.ycombinator.com/item?id=36826111) where a flat out ridiculous blog post makes it to the front page, only for the top comments to all point out how dumb and ridiculous it is. To stay within the HN guidelines, I'm going to refrain from speculating why, and I certainly know that HN audience is not some monolithic group, and that people have many different opinions. Would just like to know if others have noticed this or have other specific examples.
- Capricorn2481 3y agoFor the record, I'm not the author, nor do I agree with him. But I posted this because there was no comment section and I was curious if anyone would either 1) Agree with the article (virtually no one has) 2) Point out that the breaking changes Rich Hickey pointed out might be worth it, or not a big deal A few people have responded with sentiments of #2. Not because it's not annoying to change these things, but that it's a smaller price to pay for relatively good type systems (I'm thinking more F# than Haskell) Unfortunately, the talk Rich Hickey gives explicitly says Clojure spec doesn't have a solution for a flexible type system either, and the solution they proposed is still in alpha 4 years later. So while I appreciate the insights he offered, I'm torn between the problem of refactoring with the extra work of a finicky type system, or the work of hoping my tests are good enough to refactor my Clojure code. Types in Clojure is the number one requested feature in their surveys, year over year, and despite the reputation he has, Rich recognizes that it can really help refactoring a code base. Good variable names aren't going to help you with this. Pushing Spec2 along would be a big win without having to implement a full blown type system.
- fiddlerwoaroof 3y agoMy issue is that the author doesn’t seem to know what a breaking change is. Spec itself is annoying to me for a couple reasons, but this isn’t some novel insight of Rich Hickey’s: the claim about relaxing preconditions and strengthening postconditions is in Bertrand Meyer’s _Object Oriented Software Construction_ and is a relatively basic claim in texts about what it means for software to be correct.
- Capricorn2481 3y agoI would agree the author was wrong, and I would hazard a guess that you're a lot more well read than most of us on this kinds of stuff, because this talk is widely seen as an insightful. Is there a language that you feel solves this problem better than Spec could ever hope to?
- deleted 3y ago
- Ygg2 3y agoAuthor isn't wrong though. Most accurate and practical definition of a breaking change is - change dependents can't detect and complain. You replace O(n) algo by a O(n^2)? Breaking if someone complains. Vice versa as well. Is fixing a bug a breaking change? Yes. Ask Microsoft about porting SimCity that relied on a bug in MS-DOS.
- djur 3y agoYou can make a breaking change without changing the type signature ("do_something now uppercases and reverses the input string before using it"), and you can change the type signature without making a breaking change ("do_something now accepts None and treats it equivalently to an empty string"). The author is conflating these two scenarios.
- actionfromafar 3y agoSomeone is also sitting on the sidelines, snickering about how primitive the types and function signatures must be for that to happpen.
- anonymoushn 3y agoDo you know of a language that encodes whether a function has the capability to make a reversed uppercased copy of a string in the type of the function?
- RedNifre 3y agoMaybe Idris or Blodwen. Idris can at least define a printf function where the string must contain the right template, i.e. printf("number %i text %s", 5, "hey") compiles, but printf("number %s text %s", 5, "hey") does not, so I'm not sure it's impossible to encode string reversal in the type signature.
- deredede 3y agoYou really don't need dependent types to do the printf thing, any language with GADTs or equivalent typing functionality can do it, see for instance OCaml's Printf module. Although you do need a little bit of magic from the compiler to get that exact syntax, which you can get rid of with dependent types.
- hansvm 3y agoVery few people write code whose output purely depends on the choice of types involved. Somebody, somewhere, has to make the choice of placing max/min/wrapping_add/wrapping_sub/wrapping_mul/... in the (u32,u32)->u32 shaped hole in your program, and placing the wrong one is probably a bug. Any caller expecting an arbitrary (u32,u32)->u32 recieving your changes shouldn't break, but not all callers have correct behavior for truly arbitrary functional dependencies. From the other side, people picked your function in particular not because it fills that type hole but because it does something in particular. If you meaningfully change the thing it does then that probably breaks the callers. The author's argument is something along the lines of (1) if you're changing the type signature, that's probably because a new implementation requires it, and (2) if the new implementation is sufficiently different to warrant a change in the type signature then it's going to break somebody. I don't particularly like the example since in my experience somebody adding a nullable input will leave the old behavior alone and just wants to call the same function somewhere else, so it probably actually is a non-breaking change for all existing callers, but it's not hard to imagine stronger examples where the author would be right.
- camgunz 3y ago"Breaking change" means it changes existing behavior. As in I used to get 'banana' when I passed 'b' and now I get 'bicycle'. If I still get 'banana', you didn't break anything.
- hansvm 3y agoIt sounds like we agree.
- layer8 3y agoNo, a breaking change is if the interface contract changes in a non-backward compatible way. If the interface contract never promised you to get “banana”, and you still relied on that behavior, then yes your client code may break, but that’s on your side of the program, and your fault for relying on unwarranted assumptions, and not a breaking change in the library you called. The purpose of interface contracts is exactly to precisely specify what can change and what can’t change without breaking clients, and conversely which assumptions clients can or can’t make. This is a precise give and take. The clients are expected to play by those rules just like the implementations of the interface are expected to maintain their promises — nothing more, nothing less.