[HN Gopher] In Which I Claim Rich Hickey Is Wrong (2020) ___________________________________________________________________ In Which I Claim Rich Hickey Is Wrong (2020) Author : Capricorn2481 Score : 17 points Date : 2023-07-23 21:38 UTC (1 hours ago) (HTM) web link (blog.jonstodle.com) (TXT) w3m dump (blog.jonstodle.com) | XVincentX wrote: | The article is wrong. Rich Hickey is talking about the surface of | the function, interface. The implementation details do not matter | in this case. | riffraff wrote: | The article is claiming that the surface and the implementation | are related. | | If client code passed a null value before, an error would have | occurred. Now it is handled with a default that the original | code was not accounting for, and this might be bad. | | Thus the change in interface _is_ a change in behavior. | yakubin wrote: | I agree with Rich Hickey here and I think this is a flaw of sum | types compared to union types. If Option<T> was a union type, T | would be a subtype of Option<T> and those changes wouldn't be | breaking. When writing Rust, I find myself repeatedly writing | implementations of the From trait for enums, just because most of | the time I actually want union types, not sum types. I think sum | types should be built on top of union types by combining union | types with a "lexically-scoped newtype", if that makes sense | (i.e. they wouldn't be built in, they would be the result of two | orthogonal features, while allowing for other combinations as | well). | | Edit: another nice consequence of this design would be None being | its own type, which can be transparently converted to an | Option<T> for any T, allowing for type inference to go in only | one direction, while still avoiding the boilerplate of | Option<T>::None. Unidirectional type inference would make for | more intelligible compiler errors. Full Hindley-Milner can get | confusing. | mjburgess wrote: | Whenever i design a lang, i just give option semantics to types | of the form `x|none` | kibwen wrote: | _> which can be transparently converted to an Option <T> for | any T, allowing for type inference to go in only one direction, | while still avoiding the boilerplate of Option<T>::None_ | | You don't need to qualify the None, the following works: | fn foo<T>(x: T) -> Option<T> { None } | lmm wrote: | Union types are horrendous in practice because they're non- | compositional. None|T is usually disjoint except when it isn't, | and it's really easy to forget that case and fail to test it | properly. | | Having used Scala extensively, None as its own type is very | much a mistake; it's not a type that you ever want and it only | serves to get in the way. | patrickthebold wrote: | > You are changing the behavior of the function. That is a | breaking change. | | That makes almost everything breaking. | nimih wrote: | Well, yes. Any time you change a function's behavior, you're | risking breaking the behavior of its callers. That's just the | nature of programming and doesn't seem like particularly | controversial statement, honestly. | Tcepsa wrote: | One of the big benefits of using functions is encapsulation: | the idea that you (as the caller) do not need to understand | how a function does what it does, you just need to know what | it does (perhaps along with some performance guarantees so | you know it's not going to do e.g. an O(n^2) operation). | Beyond that, I don't want to care and should not generally | have to care about what the function is doing. As long as I | get out (including side effects) what I expect based on what | I put in, the internal behavior of the function can change | any which way and I would not consider that breaking, because | my code--using that function--would still run just fine. | jhardy54 wrote: | Counter-point: consider the case where the body of the function | is only `return value`. | draw_down wrote: | No, he's right. he's speaking only of what is required and what | is provided, all else being equal. The what and the why are | different. | | If the function started to provide different results for the same | arguments, that's different from what he's talking about and | would be a breaking change. | | Worrying about the internals of the function is a violation of | encapsulation. We care what we provide and what we receive back. | one-more-minute wrote: | I mean, the article's logic is effectively that you can break | code without the type system helping or warning you. I don't | think Hickey would disagree with this - it's arguably a point | in his favour. | joeatwork wrote: | I think the poster means that adding null is a big, substantial | change to the interface - if you have a function that used to | not make sense with a null argument, and then it suddenly does | make sense with a null, then the function has changed in a way | observable to callers. | fiddlerwoaroof wrote: | 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 case: 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. ___________________________________________________________________ (page generated 2023-07-23 23:00 UTC)