[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)